为什么实例对象和类对象之间的方法id不相同



我想知道为什么method存在两个副本,一个用于实例对象,另一个用于类对象,为什么它是这样设计的?

class Bar():
def method(self):
pass
@classmethod
def clsmethod(cls):
pass
b1 = Bar()
b2 = Bar()
print(Bar.method,id(Bar.method))
print(b1.method,id(b1.method))
print(b2.method,id(b2.method))
print(Bar.clsmethod,id(Bar.clsmethod))
print(b1.clsmethod,id(b1.clsmethod))
print(b2.clsmethod,id(b2.clsmethod))

此设计基于描述符,特别是非数据描述符。通过定义__get__方法,每个函数恰好是一个非数据描述符:

>>> def foo():
...     pass
... 
>>> foo.__get__
<method-wrapper '__get__' of function object at 0x7fa75be5be50>

当代码中有表达式x.y时,这意味着在对象x上查找属性y。这里解释了具体的规则,其中一个规则涉及y是存储在x(或任何子类(类上的(非(数据描述符。以下是一个示例:

>>> class Foo:
...     def test(self):
...         pass
...

这里Foo.test在类Foo上查找名称test。结果是您将在全局名称空间中定义的函数:

>>> Foo.test
<function Foo.test at 0x7fa75be5bf70>

然而,正如我们上面所看到的,每个函数也是一个描述符,因此,如果您在Foo实例上查找test,它将调用描述符的__get__方法来计算结果:

>>> f = Foo()
>>> f.test
<bound method Foo.test of <__main__.Foo object at 0x7fa75bf56b20>>

我们可以通过手动调用Foo.test.__get__:来获得类似的结果

>>> Foo.test.__get__(f, type(f))
<bound method Foo.test of <__main__.Foo object at 0x7fa75bf56b20>>

该机制确保实例(通常通过self表示(作为第一个参数传递给实例方法。描述符返回一个绑定方法(绑定到执行查找的实例(,而不是原始函数。这个绑定方法在被调用时将实例作为第一个参数插入。每次执行Foo.test时,都会返回一个新的绑定方法对象,因此它们的id不同。

classmethods的情况与调用Foo.test.__get__(None, Foo)的情况类似。唯一的区别是,对于实例object.__getattribute__被调用,而对于类type.__getattribute__优先。

>>> class Bar:
...     @classmethod
...     def test(cls):
...         pass
... 
>>> Bar.test
<bound method Bar.test of <class '__main__.Bar'>>
>>> Bar.__dict__['test'].__get__(None, Bar)
<bound method Bar.test of <class '__main__.Bar'>>

相关内容

  • 没有找到相关文章

最新更新