結合メソッドと非結合メソッド


クラスの属性を参照して関数オブジェクトを返す場合、結合メソッドオブジェクトもしくは非結合メソッドオブジェクトでラッピングして返す。結合メソッドは特定のインスタンスに関連付けられ、非結合メソッドは特定のインスタンスに関連付けらていないものとなる。

>>> class Foo:
...   def foo(self, x):
...     print x
...
>>> f = Foo()
>>> f.foo    # 結合メソッド
<bound method Foo.foo of <__main__.Foo instance at 0x00DF4E18>>
>>> Foo.foo  # 非結合メソッド
<unbound method Foo.foo>
>>> f.foo('x')       # 結合メソッド呼び出しは第一引数にインスタンスが必要ない
x
>>> Foo.foo(f, 'x')  # 非結合メソッド呼び出しは第一引数にインスタンスが必要
x

また、ラップされた結合メソッド、非結合メソッドはim_class、im_func、im_selfという3つの属性を持つ。それぞれ、メソッドが定義されているクラス、メソッド自身、メソッドを結合されたインスタンスを返す。

>>> f.foo.im_class, f.foo.im_func, f.foo.im_self
(<class __main__.Foo at 0x00DDA3C0>, <function foo at 0x00DD1AF0>, <__main__.Foo instance at 0x00DF4E18>)
>>> Foo.foo.im_class, Foo.foo.im_func, Foo.foo.im_self
(<class __main__.Foo at 0x00DDA3C0>, <function foo at 0x00DD1AF0>, None)

クラスメソッドも同様である。但し、クラスメソッドの結合メソッドの結合された第一引数がインスタンスオブジェクトでなく、クラスオブジェクトとなる。

>>> class Foo:
...   @classmethod
...   def foo_classmethod(cls):
...     print cls
...   @staticmethod
...   def foo_staticmethod(x):
...     print x
...   def foo_method(self):
...     print self
...
>>> f = Foo()
>>> f.foo_classmethod
<bound method classobj.foo_classmethod of <class __main__.Foo at 0x00DDA450>>
>>> f.foo_staticmethod
<function foo_staticmethod at 0x00DD1A70>
>>> f.foo_method
<bound method Foo.foo_method of <__main__.Foo instance at 0x00E30710>>
>>>
>>> f.foo_classmethod.im_self is Foo
True
>>> f.foo_method.im_self is f
True

また、結合メソッドや非結合メソッドがラップされるのはクラスが定義されたタイミングでなく、あくまでも属性が検索されるタイミングである。また、「結合メソッド、非結合メソッド」と「それ以外の関数」は、inspect.ismethodでチェックできる。

>>>class C: pass
...
>>> o = C()
>>> def foo(self):
...     return 'foo'
...
>>> def bar():
...     return 'bar'
...
>>> C.foo = foo  # もしくは、setattr(C, 'foo', foo)でもOK
>>> o.bar = bar
>>> o.foo()
'foo'
>>> o.bar()
'bar'
>>>
>>> import inspect
>>> inspect.ismethod(o.foo), hasattr(o.foo, 'im_self')  # o.fooは結合メソッド
(True, True)
>>> inspect.ismethod(o.bar), hasattr(o.bar, 'im_self')  # o.barは結合メソッドではなく単なる関数
(False, False)

参考:


追記(2007/11/9):

>>> import new
>>> o.foo = new.instancemethod(foo, o, o.__class__)
>>> o.foo()
'foo'
>>> inspect.ismethod(o.foo)
True

で親クラスCに影響を与えずにインスタンスoに固有のメソッド(特異メソッド)を追加できる。

参考: