読者です 読者をやめる 読者になる 読者になる

Pythonのアクセス制限と抽象クラス&インターフェイスについてのまとめ

Python

アクセス制限について

Javaに関して

Javaのアクセス修飾子については、public、private、protected、無名の種類があります。アクセス修飾子についてはnextindex.net - がまとまっているようなので、そこを見てみるといいでしょう。各所にて書かれているので僕がいうまでもないと思いますが、オブジェクト指向プログラミングにおいて、アクセス制限はカプセル化を実現するために必要なものです。以下に簡単にまとめたものを掲載してみます。先ほどのところを参考に書かせてもらっています。

private 同一クラス内からのアクセスのみ。
無名 同じパッケージ内からしか呼び出せない。
protected 同じパッケージか、そのサブクラスからしか呼び出せない。
public どこからでも呼び出せる。

このようにオブジェクト指向プログラムにおいて重要なアクセス制御ですが、Pythonではどのように実現されているのだろうということで、調べてみました。

Pythonに関して

Pythonのアクセス制御に関しては、フィールド*1やメソッドの前にアンダースコア(『_』のこと)をつけることによって実現できるようです。この実現の仕方には2つ方法があるようです。2つというのはアンダースコアを一つつけるやる方と、二つつけるやり方。

  • アンダースコアを一つつける

みんなのPythonによれば

Pythonでは、名前の先頭にアンダースコア(_)が一つついたアトリビュートやメソッドはクラスの内部だけで利用するためにある、というルールのようなものがあります。クラスの機能を使う人は、「_size」のような名前の先頭にアンダースコアが一つついたメソッドを見れば、このアトリビュートは外部から書き換えてはいけないのだ、と知ることができるわけです。

とあります。

  • アンダースコアを二つつける

同じくみんなのPythonによれば

より厳しくアトリビュートやメソッドへのアクセスを制限したい場合には、名前の前のアンダースコアを二つつけます。たとえば、クラスに「__size」という名前のアトリビュートを設定するとします。すると、クラスの外部からは「__size」という名前でこのアトリビュートにアクセスできなくなります。

とあります。しかし、この方法では実は完全にアトリビュートやメソッドを隠すことはできません。ある法則によって、単純にはアクセスできないような名前に変換されてしまうだけで、方法を知っていれば、アクセスできてしまうようです。

アクセス制限についての感想と考察

考察

このようにPythonではアンダースコア一つで(紳士的に)「このメソッドやアトリビュートには触らないでね」ということを伝え、アンダースコア二つで(擬似的に)アトリビュートやメソッドに触れないようになっています。Javaが物理的に*2触らせないことを考えると、Pythonはアクセス制御に関しては限定的な機能しかもたらさないようです。このように書くとPythonを否定しているように聞こえますが、Zopeを始め、様々なアプリケーションにおいて特に問題なく開発が進められており、カプセル化を言語の機能として実装しなくても、カプセル化と同様のことができる、という証明になっているとみんなのPythonには書いてありました。

感想

僕がJavaから入ったこともあり、Pythonのアクセス制限には「ん?」と疑問を持ったことは確かです。でも、みんなが制約を守ればどうにか実現できそうな気もするし*3、気にせず作りたいときもあるような気がするので、その付近は好みの問題かなと思いました。

ただ、一点分からない点がありました。アンダースコアを一つ使うときと二つ使うときの勘所、というか使い分け。両方とも完全でないなら、僕は一つに絞って使いたいなと思う性分なのですが、Python使いの人はどのように使い分けているのでしょうか?詳しい人、教えていただけたら幸いです。

抽象クラスとインターフェイスについて

抽象クラスとインターフェイスについては調べた限りでは、Pythonの言語レベルの機能としては実装されていないようです*4

抽象クラスについて

しかし、抽象クラスの役割というのは(超)大雑把に言ってしまえば、Subclass Responsibility(宣言だけしておいて実装はサブクラスに任せること)を持たせるということです。サブクラスに任せることができればいいということを考えると、次のようなコードを考えることで(擬似)抽象クラスのようなものを作る事ができます。

class Hoge:
    def do_something(self):
        raise NotImplementedError
class HogeHoge(Hoge):
    def do_something(self):
        〜♪具体的に実装しましょう♪〜

NotImplementedErrorを無条件に投げさせるのがポイント。実装しないで使おうとするとNotImplementedErrorが起こるので、これでやりたいことはできるかなという感じです。

インターフェイスについて

上のように(擬似的に)抽象クラスを体現することはできました。では、インターフェイスについてはどうなのでしょう?抽象クラスのときのように考えればすべてのメソッドに関してNotImplementedErrorを投げるようにすればいいのかなと考えたりしましたが、何か違うような?
いろいろ考えていたのですが、mixiのコミュニティにてzope.interfaceという拡張モジュールがあることを教えてもらいました。回答してくださった方ありがとうございます。

英語なので、なかなか大変なのですが、岩田さんという方がREADME.txtの翻訳をされているようです。

…が、知ったはいいもののまだあまり読めていない&理解していないということで、とりあえずこの付近で。ただ、(感謝!!とか書いてるのに、あれで申し訳ないんですが)2007年4月16日現在Googleにて「zope.interface」と検索したところ295件しか返ってきませんでした。デザパタ本のサンプルをPythonに移植するとき、この辺の扱いを、どのようにしたらいいかなと考えています。Pythonのその辺の事情に詳しい方がいらっしゃれば、教えてください。

Pythonプロフェッショナルプログラミング 第2版

Pythonプロフェッショナルプログラミング 第2版

*1:Pythonではアトリビュートっていうのが普通なのかな?

*2:といっても現実にそのものがあるわけではないですが

*3:先ほどあげたようなZopeの例とか

*4:Pythonが動的に型をつけることと関係あるのかしらん?