スタティックメソッドは要らない子? - その2
http://d.hatena.ne.jp/atsuoishimoto/20100807/1281169026 を書いたあとに、スタティックメソッドの使い道を思いついた。やや人工的なシチュエーションではあるが…
こんなコードを考えてみよう
def _callexit(): sys.exit() class Foo(object): shutdown = _callexit Foo().shutdown()
このコードは動作しない。 Foo().shutdown()
のところで、 TypeError: func() takes no arguments (1 given)
と怒られてしまう。クラスに関数オブジェクトを登録すると、それはインスタンスのメソッドとみなされ、必ず self
付きで呼び出されてしまうのだ。
従って、たとえばクラスごとに外部からコールバック関数などを設定して利用するようなパターンでは、コールバック関数を登録する時にstaticmethod
を使って
class Foo(object): @classmethod def set_callback(cls, callback): cls.callback = staticmethod(callback)
とすると良いだろう。
ところで、
def _callexit(): sys.exit() class Foo(object): shutdown = _callexit
はエラーとなるが
class Foo(object): shutdown = sys.exit Foo().shutdown()
は正常に動作する。なにが違うのだろうか?
>>> type(_callexit) <type 'function'> >>> type(sys.exit) <type 'builtin_function_or_method'>
そう、Pythonで書いた普通の関数 _callexit()
は function
型だが、sys.exit()
はC言語で書かれた組み込み関数で、また別の種類のオブジェクトなのである。そして組み込み関数はインスタンスのメソッドとは見なされないため、余計な引数の追加なしで呼び出すことが出来るのだ。 同様な理屈で、
class Shutdown: def __call__(self): sys.exit() class Bar: shutdown = Shutdown()
もOKだ。
だが、
class Foo(object): shutdown = sys.exit
は最良のコードとは言い難い。動くことは動くが、いつの日かsys.exit()
が普通の関数になってしまうかも知れないし、あるいはsys.exit()
ではなく、別の関数オブジェクトを呼び出すように変更されるかも知れない。JythonやPyPyなど、CPython以外での挙動も気になるところだ。
そこで、もしこんなコードがあったら、こっそりと
class Foo(object): shutdown = staticmethod(sys.exit)
と書き直しておくと良いだろう。 staticmethod
でラップしておけば中身がどんな種類のオブジェクトであってもちゃんと呼び出すことが出来るし、見た目上のシンプルさも保つことが出来る。