OSTRACISM CO.

ScalaとOCamlとF#とPythonと...

PythonでOOP

 Pythonのクラスオブジェクトの初期化処理は__init__メソッドに書くと思っていた。いや、実際に__init__がその役割を持っているのは事実だが、なんと、クラスを継承した場合、継承されたクラスの__init__は何もしなけりゃ呼ばれない。えぇぇぇえ。なんでこんな仕様なのか、色々すったもんだがあって今更変えられないとかパラメタ付きはどうするとか面倒があるんだろうけど、ともかくきっぱり呼ばれない。一番元のクラスを定義するときにobjectを継承するとsuper()関数が使えたりするが、super(ClassName, self).__init__()なんて記述は呼ばれないからこそ書かなくちゃいけないわけで、たいした解決にはならない。しかも、多重継承した場合、super().__init__()では最も左に(最優先の位置に)記述されたクラスの__init__しか呼ばれない。いったいどうしろというのだ。クラスの意義の一つには実装の隠蔽があるはずなんだが、objectを継承しているのかとか、__init__が定義されているのかとか使う側がソースレベルで知らないといけないってのはどう考えても間違っているだろ、これ。

 他人の書いたPythonプログラムのメンテで困ったことになる。継承されちゃってるクラスにインスタンス変数を用意したかったのだが、__init__メソッドがない。ってことは継承してる先では__init__を呼んだりはしてないわけで、__init__を加えるとしたら継承してる先(当然複数箇所)全部に__init__を呼ぶ処理を加えなくちゃいけない。いや、そもそも継承してる先にも__init__なんてないので、ってところで気付く。まるでIOモナドの汚染じゃないか。__init__汚染とでも名付けようか。

 しかたがないので、インスタンス変数アクセスにgetattr()関数を使う。格好悪いし馬鹿馬鹿しいが、そういう言語でそういうシステムなのだと思うことにする。

 迷惑ついでに迷惑なのが__call__メソッド。インスタンスを関数として呼んだ時に来るエントリー。困ったねぇ、検索性ゼロじゃん。どこで呼んでるかのキーワードがなくなってしまう。機能の一意性とかメンテナンス性とか捨てて「ヘイ、こんな風に書けちゃうんだぜ、格好良い」って思いたけりゃ使えばいいけど、仕事で使うのは勘弁してくれ。

 加えて最悪なのは__getitem__メソッド。インスタンスをマップとして呼んだ時に来るエントリー。これも検索性ゼロだよ。どうやってメンテすんだよ。しかも__getitem__メソッドだけでも使えるのだが、その状態でインスタンスを参照すると{}になる。これ、if文でFalse扱いだぞ。

 教訓。PythonでOOPは真似事程度にしておけ。あと、1000行超えるとか複数ファイルになるなら別の、できたら静的型付けの言語にしておけ。


2015.02.23


「インデックス」へ戻る


OSTRACISM CO.

OSTRA / Takeshi Yoneki