Python 中的 is 和 id
当前位置:以往代写 > Python教程 >Python 中的 is 和 id
2019-06-14

Python 中的 is 和 id

Python 中的 is 和 id

(ob1 is ob2) 等价于 (id(ob1) == id(ob2))

  首先id函数可以得到工具的内存地点,假如两个工具的内存地点是一样的,那么这两个工具必定是一个工具。和is是等价的。Python源代码为证。

static PyObject *
 cmp_outcome(int op, register PyObject *v, register PyObject *w)
{
 int res = 0;
 switch (op) {
 case PyCmp_IS:
  res = (v == w);
 break;
 case PyCmp_IS_NOT:
res = (v != w);
 break;

  可是请看下边代码的这种环境怎么会呈现呢?

In [1]: def bar(self, x):

…:     return self.x + y

…: 

In [2]: class Foo(object):

…:     x = 9

…:     def __init__(self ,x):

…:         self.x = x

…:     bar = bar

…:     

In [3]: foo = Foo(5)

In [4]: foo.bar is Foo.bar

Out[4]: False

In [5]: id(foo.bar) == id(Foo.bar)

Out[5]: True

  两个工具用is判定是False,用id判定却是True,这与我们已知的事实不符啊,这种现象该如何表明呢?碰着这种环境最好的办理要领就是挪用dis模块去看下两个较量语句到底做了什么。

In [7]: dis.dis("id(foo.bar) == id(Foo.bar)")

          0 BUILD_MAP       10340

          3 BUILD_TUPLE     28527

          6 <46>           

          7 DELETE_GLOBAL   29281 (29281)

         10 STORE_SLICE+1 

         11 SLICE+2       

         12 DELETE_SUBSCR  

         13 DELETE_SUBSCR  

         14 SLICE+2       

         15 BUILD_MAP       10340

         18 PRINT_EXPR     

         19 JUMP_IF_FALSE_OR_POP 11887

         22 DELETE_GLOBAL   29281 (29281)

         25 STORE_SLICE+1 

 

In [8]: dis.dis("foo.bar is Foo.bar")

          0 BUILD_TUPLE     28527

          3 <46>           

          4 DELETE_GLOBAL   29281 (29281)

          7 SLICE+2       

          8 BUILD_MAP        8307

         11 PRINT_EXPR     

         12 JUMP_IF_FALSE_OR_POP 11887

         15 DELETE_GLOBAL   29281 (29281)

  真实环境是当执行.操纵符的时候,实际是生成了一个proxy工具,foo.bar is Foo.bar的时候,两个工具顺序生成,放在栈里对较量,由于地点差异必定是False,可是id(foo.bar) == id(Foo.bar)的时候就差异了,首先生成foo.bar,然后计较foo.bar的地点,计较完之后foo.bar的地点之后,就没有任何工具指向foo.bar了,所以foo.bar工具就会被释放。然后生成Foo.bar工具,由于foo.bar和Foo.bar所占用的内存巨细是一样的,所以又刚好重用了原先foo.bar的内存地点,所以id(foo.bar) == id(Foo.bar)的功效是True。

  下面内容由邮件Leo Jay大牛提供,他表明的越发通透。

  用id(expression a) == id(expression b)来判定两个表达式的功效是不是同一个工具的想法是有问题的。

  foo.bar 这种形式叫 attribute reference [1],它是表达式的一种。foo是一个instance object,bar是一个要领,这个时候表达式foo.bar返回的功效叫method object [2]。按照文档:

When an instance attribute is referenced that isn’t a data attribute, 

its class is searched. If the name denotes a valid class attribute 

that is a function object, a method object is created by packing 

(pointers to) the instance object and the function object just found 

together in an abstract object: this is the method object.

  foo.bar自己并不是简朴的名字,而是表达式的计较功效,是一个 method object,在id(foo.bar)这样的表达式里,method object只是一个姑且的中间变量罢了,对姑且的中间变量做id是没有意义的。

  一个更明明的例子是,

print id(foo.bar) == id(foo.__init__)

  输出的功效也是True

  看 id 的文档[3]:

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.

  只有你能担保工具不会被销毁的前提下,你才气用 id 来较量两个工具。所以,假如你非要比的话,得这样写:

fb = foo.bar 

Fb = Foo.bar 

print id(fb) == id(Fb)

  即把两个表达式的功效绑定到名字上,再来比是不是同一个工具,你才气获得正确的功效。

#p#分页标题#e#

  is表达式 [4] 也是一样的,你此刻获得了正确的功效,完全是因为 CPython 此刻的实现细节抉择的。此刻的is的实现,是阁下双方的工具都计较出来,然后再较量这两个工具的地点是否一样。万一哪天改成了,先算左边,生存地点,把左边释放掉,再算右边,再较量的话,你的is的功效大概就错了。官方文档里也提到了这个问题 [5]。我认为正确的要领也是像id那样,先把阁下双方都计较下来,并显式绑定到各自的名字上,然后再用is判定。

    关键字:

在线提交作业