我发现以下示例有点令人惊讶:
>>> class Foo:
def blah(self):
pass
>>> f = Foo()
>>> def bar(self):
pass
>>> Foo.bar = bar
>>> f.bar
<bound method Foo.bar of <__main__.Foo object at 0x02D18FB0>>
我希望绑定方法与每个特定实例相关联,并在构造时放入其中。似乎合乎逻辑的是绑定方法必须对每个实例都不同,以便它知道将哪个实例传递给底层函数 - 实际上:
>>> g = Foo()
>>> g.blah is f.blah
False
但是我对这个过程的理解显然是有缺陷的,因为我不希望将一个函数分配给一个类属性会把它放在已经由创建的实例中。
所以,我的问题有两个 -
答案 0 :(得分:11)
你想要打击你的想法,试试这个:
f.blah is f.blah
没错,每次访问时,实例方法包装器都是不同的。
实际上,实例方法是描述符。换句话说,f.blah
实际上是:
Foo.blah.__get__(f, type(f))
方法实际上并不存储在实例上;它们存储在类中,并且动态生成方法包装器以将方法绑定到实例。
答案 1 :(得分:8)
实例不“包含”该方法。查找过程在您访问foo.bar
时动态发生。它检查实例是否具有该名称的属性。因为它没有,它在类上查找,然后它找到该类在那时具有的任何属性。请注意,方法在这方面并不特别。如果设置Foo.bar = 2
,您会看到相同的效果;在此之后,foo.bar
将评估为2。
语言保证的是属性查找以这种方式进行:首先是实例,然后是在实例上找不到属性的类。 (special methods implicitly invoked via operator overloading, etc.的查找规则不同。)
只有直接为实例分配属性才会屏蔽类属性。
>>> foo = Foo()
>>> foo.bar
Traceback (most recent call last):
File "<pyshell#79>", line 1, in <module>
foo.bar
AttributeError: 'Foo' object has no attribute 'bar'
>>> foo.bar = 2
>>> Foo.bar = 88
>>> foo.bar
2
以上所有内容都与绑定/未绑定方法不同。 Python中的类机制使用descriptor protocol,因此当您访问foo.bar
时,会动态创建新的绑定方法实例。这就是为什么你在不同的对象上看到不同的绑定方法实例。但请注意,这些绑定方法基本上依赖于您在类中编写的方法所定义的相同代码对象:
>>> foo = Foo()
>>> foo2 = Foo()
>>> foo.blah.im_func.__code__ is foo2.blah.im_func.__code__
True