当前位置: 首页 > 面试题库 >

绑定和未绑定方法对象的id()-有时对于不同的对象相同,有时对于相同的对象不同

董阳平
2023-03-14
问题内容

我尝试过一些关于绑定和未绑定方法的代码。当我们调用它们时,我认为它们都会返回对象。但是,当我id()用于获取一些信息时,它返回的内容我并不理解。

IDE:Eclipse

插件: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

输出是…

5671672

5671672

5671672

5669368

为什么#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。因此,您将获得两个不同的值。

请参阅有关文档id()

返回对象的“身份”。这是一个整数(或长整数),在此对象的生存期内,此整数保证是唯一且恒定的。
具有非重叠生存期的两个对象可能具有相同的id()

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

强调我的。

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

>>> 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>>

请注意,在Python 3中,删除了整个未绑定/绑定方法的区别;您将获得一个函数,在此之前,您将获得一个未绑定的方法,否则将获得一个方法,在此方法中,方法
始终是 绑定的:

>>> 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>>

此外,Python
3.7添加了一个新的LOAD_METHOD-CALL_METHOD操作码对,以精确地替换当前的LOAD_ATTRIBUTE-CALL_FUNCTION操作码对,以避免每次都创建一个新的方法对象。这种优化变换executon路径instance.foo()type(instance).__dict__['foo'].__get__(instance, type(instance))()type(instance).__dict__['foo'](instance)“手动”,所以在该实例通过直接向功能对象。



 类似资料:
  • 问题内容: 在以下代码中,我不明白为什么当它属于两个不同的对象时具有相同的ID? 问题答案: 我认为这是正在发生的事情: 取消引用时,将在内存中创建其副本。该存储位置由以下位置返回 由于没有引用到刚刚创建的方法的副本,因此GC将其回收,并且该内存地址再次可用 取消引用时,将在相同的内存地址(可用)中创建它的副本,您可以再次使用该地址。 第二个副本是GCd 如果您要运行一堆其他代码并再次检查实例方法

  • 问题内容: 我以为运算符检查对象的相等性。但事实并非如此: 问题答案: *Python *将相同的内存 位置用于方法和,这是*两个对象,它们的生命周期不重叠,因此对它们返回相同的标识。请参阅下面的详细说明。 从is运算符的文档中: 运算符是否测试对象标识:并且仅当x和y是同一对象时,x is y才是true。 从ID的文档中 返回对象的“身份”。这是一个整数(或长整数),在此对象的生存期内,此整数

  • 问题内容: 我正在练习继承。 我有两个相似的类,我想将其同化为一个数组,因此我想将Object类用作超类,因为所有内容都是Object的子类。 因此,例如,我将T类和CT类放入一个名为all的数组中,如下所示: 我跳过了声明,因为那不是我的问题。 当我希望使用循环在数组内调用函数时,我真正的问题就变成了: T和CT分别涉及的类都具有beingShot方法,该方法是公共的。 Eclipse建议将它们

  • 问题内容: 让我们创建两个列表: 出: 我创建了两个独立的列表,输出显示了两个不同的内存地址。这不足为奇。但是,现在让我们在没有分配的情况下做同样的事情: 出: 还有第二次: 出: 我不确定如何解释这一点。为什么这两个未命名列表具有相同的内存地址? 问题答案: 从文档: 返回对象的“身份”。这是一个整数,可以保证在此对象的生存期内唯一且恒定。 具有不重叠生存期的两个对象可能具有相同的id()值。

  • 问题内容: 一个人如何处理一个javascript对象数组,例如: 并通过将值相加来合并重复的键。为了得到这样的东西: 我尝试迭代并添加到新数组,但这没有用: 问题答案: 您应该使用属性将未找到的每个对象分配给结果。 如果找到它,则需要添加它。 另外,部分问题是您正在重用变量来引用的值,因此您失去了对该对象的引用。

  • 问题内容: 一个人如何处理一个javascript对象数组,例如: 并通过将值相加来合并重复的键。为了得到这样的东西: 我尝试迭代并添加到新数组,但这没有用: 问题答案: 您应该使用属性将未找到的每个对象分配给结果。 如果找到它,则需要添加它。 另外,部分问题是您正在重用变量来引用的值,因此您失去了对该对象的引用。