2012-04-18 70 views
6

这是CPython 2.7.2和3.2.2。为什么一些引用``x.y``的表达式改变``id(x.y)``?

假设我们定义如下Classobj

class Class(object): 

    def m(self): 
     pass 

    @property 
    def p(self): 
     return None 

    @staticmethod 
    def s(): 
     pass 

obj = Class() 

短版

为什么每个print()下面的代码输出False

print(Class.__dict__ is Class.__dict__) 
print(Class.__subclasshook__ is Class.__subclasshook__) 
print(Class.m is Class.m) 

print(obj.__delattr__ is obj.__delattr__) 
print(obj.__format__ is obj.__format__) 
print(obj.__getattribute__ is obj.__getattribute__) 
print(obj.__hash__ is obj.__hash__) 
print(obj.__init__ is obj.__init__) 
print(obj.__reduce__ is obj.__reduce__) 
print(obj.__reduce_ex__ is obj.__reduce_ex__) 
print(obj.__repr__ is obj.__repr__) 
print(obj.__setattr__ is obj.__setattr__) 
print(obj.__sizeof__ is obj.__sizeof__) 
print(obj.__str__ is obj.__str__) 
print(obj.__subclasshook__ is obj.__subclasshook__) 
print(obj.m is obj.m) 

(这是在Python 2;对于Python 3中,省略了print()Class.m并添加类似于那些为obj.__eq__obj.__ge__obj.__gt__obj.__le__obj.__lt__obj.__ne__

,为什么,相比之下,以下代码是否为print()输出True

print(Class.__class__ is Class.__class__) 
print(Class.__delattr__ is Class.__delattr__) 
print(Class.__doc__ is Class.__doc__) 
print(Class.__format__ is Class.__format__) 
print(Class.__getattribute__ is Class.__getattribute__) 
print(Class.__hash__ is Class.__hash__) 
print(Class.__init__ is Class.__init__) 
print(Class.__module__ is Class.__module__) 
print(Class.__new__ is Class.__new__) 
print(Class.__reduce__ is Class.__reduce__) 
print(Class.__reduce_ex__ is Class.__reduce_ex__) 
print(Class.__repr__ is Class.__repr__) 
print(Class.__setattr__ is Class.__setattr__) 
print(Class.__sizeof__ is Class.__sizeof__) 
print(Class.__str__ is Class.__str__) 
print(Class.__weakref__ is Class.__weakref__) 
print(Class.p is Class.p) 
print(Class.s is Class.s) 

print(obj.__class__ is obj.__class__) 
print(obj.__dict__ is obj.__dict__) 
print(obj.__doc__ is obj.__doc__) 
print(obj.__module__ is obj.__module__) 
print(obj.__new__ is obj.__new__) 
print(obj.__weakref__ is obj.__weakref__) 
print(obj.p is obj.p) 
print(obj.s is obj.s) 

(这是在Python 2;为Python 3,对于Class.__eq__Class.__ge__Class.__gt__Class.__le__Class.__lt__Class.__ne__添加类似于print()秒,Class.m

长版

如果我们连续询问id(obj.m)两次,然后我们得到相同的ID两次。

>>> id(obj.m) 
139675714789856 
>>> id(obj.m) 
139675714789856 

但如果我们要求id(obj.m),然后一些表达式包含对obj.m,然后再id(obj.m),该ID的变化有时(但不总是)。如果它改变了,那么如果我们再次要求id(obj.m),则ID有时(但并非总是)变回原始值。如果它没有变回,那么看起来重复中间表达式会导致ID在两个值之间交替。

这里是一些例子,其中id(obj.m)不会改变:

>>> print(obj.m); id(obj.m) 
<bound method Class.m of <__main__.Class object at 0x7f08c96058d0>> 
139675714789856 
>>> obj.m is None; id(obj.m) 
False 
139675714789856 
>>> obj.m.__func__.__name__; id(obj.m) 
'm' 
139675714789856 
>>> obj.m(); id(obj.m) 
139675714789856 

这里是一个例子,其中id(obj.m)变更,然后变回:

>>> obj.m; id(obj.m); id(obj.m) 
<bound method Class.m of <__main__.Class object at 0x7f08c96058d0>> 
139675715407536 
139675714789856 

这里是一个例子,其中id(obj.m)变化,然后不改回:

>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675715407536 
139675715407536 

这里是同样的表情,反复几次后证明交替行为:

>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675714789856 
139675714789856 
>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675715407536 
139675715407536 
>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675714789856 
139675714789856 

因此我的整个问题是

  • 什么样的属性,可能会改变他们的身份表达的副作用那些不会修改这些属性?
  • 什么样的表达式触发这样的改变?
  • 导致这种变化的机制是什么?
  • 在什么条件下回收过去的身份?
  • 为什么不是无限循环的第一个身份,这将避免所有这些并发症?
  • 是否有任何记录?

回答

5

什么类型的属性可能会改变它们的身份作为不修改这些属性的表达式的副作用?

属性,或更准确地说实现了descriptor protocol的对象。例如,Class.__dict__不是dict,而是dictproxy。显然,每次请求时都会重新生成此对象。为什么?可能要减少创建对象的开销,直到有必要这样做。但是,这是一个实现细节。重要的是__dict__按照记录工作。

即使是普通的实例方法也是使用描述符处理的,这就解释了为什么obj.m is not obj.m。有趣的是,如果你做obj.m = obj.m永久存储该实例的方法包装,然后obj.m is obj.m。 :-)

什么样的表达式触发这样的改变?

对属性的任何访问都可以触发描述符的方法,并且此方法可以始终返回相同的对象或每次返回一个不同的对象。

什么是导致这种变化的机制?

属性/描述符。

在什么条件下回收过去的身份?

不确定你的意思是“回收”。你的意思是“处置”或“重用”?在CPython中,对象的id是它的内存位置。如果两个对象在不同时间在同一个内存位置结束,它们将具有相同的id。因此,在不同时间(即使在一个语句中)具有相同的相同id的两个参考不一定是相同的对象。其他Python实现使用不同的规则来生成id。例如,我相信Jython使用递增整数,这可以提供更清晰的对象标识。

为什么不是无限循环的第一个身份,这将避免所有这些并发症?

推测使用描述符有一些优点。 Python解释器的源代码可用;看看,如果你想知道更多的细节。

是否有这种记录?

不。这些是CPython解释器的特定于实现的细节,不应该依赖。其他Python实现(包括CPython的未来版本)可能并最有可能会表现不同。例如,在2.x和3.x CPython之间存在显着差异。

2

当您编写x.y时,会创建一个绑定或未绑定的方法。这是一个新的对象。它可以在任何地方记忆。如果你写x.y并且不使用结果,它的refcnt可以被归零。这意味着内存可用,并且可以在下一个x.y中使用,可能在相同的位置,但不一定。

请注意CPython关于对象标识的保证很少(即保证只有一个);否则,你所看到的很多是可能改变的任意实现选择。

+0

@ kindall的回答解决了我的大部分问题,但是这解决了有关回收ID的子问题。谢谢。 – nisavid 2013-05-21 16:35:00

相关问题