super(self.__class__, self) は危険 (Python 2.x)
Python2.xのsuperのお話です*1。
まさかの今の今まで知らなかったので戒めとして記録しておきます。
結論
結論から言えば「2回以上継承されうるクラスでは*2、super関数で親クラスを参照する場合は、super(self.__class__, self)
ではなく、ちゃんとsuper(クラス名, self)
と表記しなければならない。」ということです。
そもそもsuper関数とは?
super関数は、ドキュメントによればメソッドの呼び出しを type の親または兄弟クラスに委譲する、プロキシオブジェクト
を返す関数です。イメージとしては、子クラスのインスタンスがbindされている親クラスのインスタンス的な感じです*3。子クラスから親クラスの同名メソッドを呼び出す時に使われます。
ただ、実際の所、Pythonではsuper関数がなくても親クラスのメソッドを呼べたりします。
class A(object): def print_class(self): print "A:", self.__class__ class B(A): def print_class(self): print "B:", self.__class__ A.print_class(self) class C(B): def print_class(self): print "C:", self.__class__ B.print_class(self) if __name__ == "__main__": A().print_class() B().print_class() C().print_class()
実行結果
A: <class '__main__.A'> B: <class '__main__.B'> A: <class '__main__.B'> C: <class '__main__.C'> B: <class '__main__.C'> A: <class '__main__.C'>
何故これで良いのかというと、Pythonのメソッドにはboundとunboudの2種類があるからです。boundなメソッド*4は、インスタンスが第一引数selfにbindされているメソッドのことですので、適当な変数に代入してもbindされた状態でメソッド呼び出しが出来たりします。最近は、JavaScriptでもthisをbindするのが、しばしば使われるテクニックになっていると思いますが、それと要領は同じです。これ以上の詳細はここでは省略します。
話を戻すと、だったらsuper要らないんじゃない?となりそうなところですが、superにはsuperの良さがあります。それが、多重継承のいわゆるダイヤモンド継承の時に、(中略)なのですが、多重継承が許されるのはsh(ryなので*5、詳細は記事末尾の参考文献を見てください*6。
super(self.__class__, self) とは?
Python2.xにおいて、子クラスから親クラスのメソッドを呼び出す際にしばしば使われるイディオムです。このイディオムは、self.__class__
を使うことで、親クラスを明示的に指定する必要がなくなるので、クラス名を変えても、全てのsuper部分を修正する必要がないのが便利ということで使われるようになったのだと思います。
super(self.__class__, self) が危険な理由
self.__class__
が何を指すのかがポイントです。
# -*- coding: utf-8 -*- # 出力がえらいこっちゃになるので最大再帰回数を変更 import sys sys.setrecursionlimit(5) class A(object): def print_class(self): print self.__class__ class B(A): def print_class(self): super(self.__class__, self).print_class() class C(B): def print_class(self): super(self.__class__, self).print_class() if __name__ == "__main__": B().print_class() # <class '__main__.B'> C().print_class() # infinite recursion!
子クラスでsuper(self.__class__, self)
した時、親クラスのメソッドでのself.__class__
は子クラスのままなので、擬似コードで書けば以下の様になります。
class A(object): def print_class(self): print self.__class__ class B(A): def print_class(self): super(C, self).print_class() # = B.print_class(self) class C(B): def print_class(self): super(self.__class__, self).print_class() if __name__ == "__main__": C().print_class() # infinite recursion!
ということで、注意しましょう!おしまい。
参考文献
- Class method differences in Python: bound, unbound and static - Stack Overflow
- unbound/bound methodに関してのQ&A。
- Python の super のメリットとデメリット - methaneのブログ
- superの詳説。
- Python: super(self.__class__, self) じゃダメなんですか? | CUBE SUGAR STORAGE
- 偶然このサイトを開いて、えっ、ってなった(汗。
- Pythonでsuper(self.__class__, self)は使うな - みょんさんの。
- ダメな理由が詳しく説明されています。
- python - How to avoid infinite recursion with super()? - Stack Overflow