绑定和未绑定方法对象的 id() --- 有时对不同的对象相同,有时对同一对象不同 [英] id()s of bound and unbound method objects --- sometimes the same for different objects, sometimes different for the same object

查看:19
本文介绍了绑定和未绑定方法对象的 id() --- 有时对不同的对象相同,有时对同一对象不同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经尝试了一些关于绑定和未绑定方法的代码.当我们调用它们时,我认为它们都会返回对象.但是当我使用 id() 获取一些信息时,它返回一些我不明白的东西.

IDE:Eclipse

插件:pydev

C 类(对象):def foo(self):经过cobj = C()打印 id(C.foo) #1打印 id(cobj.foo) #2a = C.foob = cobj.foo打印 id(a) #3打印 ID(b) #4

输出是...

5671672567167256716725669368

为什么#1 和#2 返回相同的id?它们不是不同的对象吗?如果我们将 C.fooconj.foo 分配给两个变量,#3 和 #4 返回不同的 id.

我认为#3 和#4 表明它们不是同一个对象,而是#1 和#2...

绑定方法的id和未绑定方法的id有什么区别?

解决方案

每当您通过 instance.name 查找方法(以及在 Python 2 中,class.name),方法对象被创建一个新的.Python 使用 描述符协议 将函数包装在方法对象中每次.

因此,当您查找 id(C.foo) 时,会创建一个新的方法对象,您检索其 id(内存地址),然后 再次丢弃该方法对象.然后您查找 id(cobj.foo),这是一个创建的新方法对象,它重用现在释放的内存地址,您会看到相同的值.然后,该方法再次被丢弃(在引用计数降至 0 时收集垃圾).

接下来,您在变量中存储了对 C.foo 未绑定方法的引用.现在内存地址没有被释放(引用计数是 1,而不是 0),你通过查找必须使用的 cobj.foo 创建一个 second 方法实例一个新的内存位置.因此你会得到两个不同的值.

请参阅文档id():

<块引用>

返回对象的身份".这是一个整数(或长整数),保证在该对象的生命周期内是唯一的和常量.生命周期不重叠的两个对象可能具有相同的id().

CPython 实现细节:这是对象在内存中的地址.

强调我的.

您可以通过类的 __dict__ 属性使用对函数的直接引用重新创建方法,然后调用 __get__ 描述符方法:

<预><代码>>>>C类(对象):... def foo(self):... 经过...>>>富人<未绑定方法C.foo>>>>C.__dict__['foo']<函数 foo 在 0x1088cc488>>>>C.__dict__['foo'].__get__(None, C)<未绑定方法C.foo>>>>C.__dict__['foo'].__get__(C(), C)<0x1088d6f90处<__main__.C对象的绑定方法C.foo>

请注意,在 Python 3 中,取消了整个未绑定/绑定方法的区别;你会得到一个函数,之前你会得到一个未绑定的方法,否则会得到一个方法,其中一个方法总是绑定:

<预><代码>>>>富人<函数 C.foo 在 0x10bc48dd0>>>>C.foo.__get__(None, C)<函数 C.foo 在 0x10bc48dd0>>>>C.foo.__get__(C(), C)<0x10bc65150处<__main__.C对象的绑定方法C.foo>

此外,Python 3.7 添加了一个新的LOAD_METHOD - CALL_METHOD 精确替换当前 LOAD_ATTRIBUTE - CALL_FUNCTION 操作码对的操作码对,以避免每次都创建新的方法对象.此优化将 instance.foo() 的执行路径从 type(instance).__dict__['foo'].__get__(instance, type(instance))()使用 type(instance).__dict__['foo'](instance),所以手动"将实例直接传递给函数对象.

I've tried some code about bound and unbound methods. When we call them, I think both of them would return objects. But when I use id() for getting some information, it returns something I don't understand.

IDE: Eclipse

Plugin: pydev

Class C(object):
    def foo(self):
        pass

cobj = C()

print id(C.foo)    #1
print id(cobj.foo) #2

a = C.foo
b = cobj.foo

print id(a)        #3
print id(b)        #4

And the output is...

5671672

5671672

5671672

5669368

Why do #1 and #2 return the same id? Aren't they different objects? And if we assign C.foo and conj.foo to two variables, #3 and #4 return the different id.

I think #3 and #4 show that they are not the same object, but #1 and #2...

What is the difference between the id of bound method, and an unbound method?

解决方案

Whenever you look up a method via instance.name (and in Python 2, class.name), the method object is created a-new. Python uses the descriptor protocol to wrap the function in a method object each time.

So, when you look up id(C.foo), a new method object is created, you retrieve its id (a memory address), then discard the method object again. Then you look up id(cobj.foo), a new method object created that re-uses the now freed memory address and you see the same value. The method is then, again, discarded (garbage collected as the reference count drops to 0).

Next, you stored a reference to the C.foo unbound method in a variable. Now the memory address is not freed (the reference count is 1, instead of 0), and you create a second method instance by looking up cobj.foo which has to use a new memory location. Thus you get two different values.

See the documentation for id():

Return the "identity" of an object. This is an integer (or long integer) which is guaranteed to be unique and constant for this object during its lifetime. Two objects with non-overlapping lifetimes may have the same id() value.

CPython implementation detail: This is the address of the object in memory.

Emphasis mine.

You can re-create a method using a direct reference to the function via the __dict__ attribute of the class, then calling the __get__ descriptor method:

>>> class C(object):
...     def foo(self):
...         pass
... 
>>> C.foo
<unbound method C.foo>
>>> C.__dict__['foo']
<function foo at 0x1088cc488>
>>> C.__dict__['foo'].__get__(None, C)
<unbound method C.foo>
>>> C.__dict__['foo'].__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x1088d6f90>>

Note that in Python 3, the whole unbound / bound method distinction has been dropped; you get a function where before you'd get an unbound method, and a method otherwise, where a method is always bound:

>>> C.foo
<function C.foo at 0x10bc48dd0>
>>> C.foo.__get__(None, C)
<function C.foo at 0x10bc48dd0>
>>> C.foo.__get__(C(), C)
<bound method C.foo of <__main__.C object at 0x10bc65150>>

Furthermore, Python 3.7 adds a new LOAD_METHOD - CALL_METHOD opcode pair that replaces the current LOAD_ATTRIBUTE - CALL_FUNCTION opcode pair precisely to avoid creating a new method object each time. This optimisation transforms the executon path for instance.foo() from type(instance).__dict__['foo'].__get__(instance, type(instance))() with type(instance).__dict__['foo'](instance), so 'manually' passing in the instance directly to the function object.

这篇关于绑定和未绑定方法对象的 id() --- 有时对不同的对象相同,有时对同一对象不同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆