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

2024-09-28 01:24:25 发布

您现在位置:Python中文网/ 问答频道 /正文

我想知道为什么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))

Tags: 对象实例selfiddef副本barpass
1条回答
网友
1楼 · 发布于 2024-09-28 01:24:25

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

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

当代码中有一个表达式x.y时,这意味着在对象x上查找属性y。具体的规则是explained here,其中一个涉及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不同

使用classmethod的情况与调用^{}的情况类似。唯一的区别是,对于实例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'>>

相关问题 更多 >

    热门问题