Methods are bound on demand, each time you access one.
Accessing the name of a function invokes the descriptor protocol, which on function objects returns a bound method.
A bound method is a thin wrapper around a function object; it stores a reference to the original function and to the instance. When calling a method object, it in turn passes the call to the function, with instance inserted as a first argument.
Methods are not created when the instance is created, so there is no extra memory required a-priori.
You can re-create the steps manually:
>>> class A:
... def __init__(self, name):
... self.name = name
... def foo(self):
... print(self.name)
...
>>> a = A('One')
>>> a.foo
<bound method A.foo of <__main__.A object at 0x100a27978>>
>>> a.foo.__self__
<__main__.A object at 0x100a27978>
>>> a.foo.__func__
<function A.foo at 0x100a22598>
>>> A.__dict__['foo']
<function A.foo at 0x100a22598>
>>> A.__dict__['foo'].__get__(a, A)
<bound method A.foo of <__main__.A object at 0x100a27978>>
>>> A.__dict__['foo'].__get__(a, A)()
One
It is only the method object that is recreated each time; the underlying function remains stable:
>>> a.foo is a.foo
False
>>> b = A('Two')
>>> b.foo is a.foo
False
>>> b.foo.__func__ is a.foo.__func__
True
This architecture also makes classmethod
, staticmethod
, and property
objects work. You can create your own descriptors, creating a whole host of interesting binding behaviours.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…