クラスの属性を参照して関数オブジェクトを返す場合、
結合メソッドオブジェクトもしくは非結合メソッドオブジェクトでラッピングして返す。
結合メソッドは特定のインスタンスに関連付けられ、
非結合メソッドは特定のインスタンスに関連付けらていないものとなる。
>>> 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
>>> o.bar = bar
>>> o.foo()
'foo'
>>> o.bar()
'bar'
>>>
>>> import inspect
>>> inspect.ismethod(o.foo), hasattr(o.foo, 'im_self')
(True, True)
>>> inspect.ismethod(o.bar), hasattr(o.bar, 'im_self')
(False, False)
以下のように、親クラスCに影響を与えずにインスタンスoに固有のメソッド(特異メソッド)を追加できる。
>>> import new
>>> o.foo = new.instancemethod(foo, o, o.__class__)
>>> o.foo()
'foo'
>>> inspect.ismethod(o.foo)
True
但し、ビルトインクラスおよびそのインスタンスに関しては属性を追加・変更・削除することが不可能なので、
ビルトインクラスのインスタンスオブジェクトに特異メソッドを追加することはできない。
ビルトインクラスの属性の挙動を変更したい場合は、以下の2通りの方法をとる。
- ビルトインクラスを継承した自前のクラスを作成してメソッド(属性)をオーバーライドする
- ビルトインクラスのインスタンスを属性に持ち、属性の機能を委譲する