我认为is
运算符会检查对象id
的相等性。但它似乎并非如此:
>>> class A(object):
... def f(): return 1
... def g(): return 2
...
>>> a = A()
>>> a.f is a.g
False
>>> id(a.f) == id(a.g)
True
答案 0 :(得分:15)
Python重用相同的内存位置而不保留对对象的其他引用,一旦id(a.f)
被评估,对对象有更多的引用,因此它是gc'然后python可以自由地重用它a.g
的内存位置。如果将方法分配给名称,您将看到不同的行为:
# creates a reference to the method f
In [190]: f = a.f
# creates a reference to the method g
In [191]: g = a.g
# cannot reuse the memory location of f as it is still referenced
In [192]: id(f) == id(g)
Out[192]: False
实际上你真的只需要存储对f的引用来查看与上面相同的行为。
In [201]: f = a.f
In [202]: id(f) == id(a.g)
Out[202]: False
您可以使用sys.getrefcount
或gc.gc.get_referrers
:
In [2]: import gc
In [3]: f = a.f
In [4]: len(gc.get_referrers(a.g)),len(gc.get_referrers(f))
Out[4]: (0, 1)
In [5]: sys.getrefcount(a.g),sys.getrefcount(f)
Out[5]: (1, 2)
你为a.g看到1的唯一原因是因为返回的计数通常比你预期的高一个,因为它包含(临时)引用作为getrefcount()的参数。
它类似于您自己的示例,在评估方法后,您仍然会引用f
,a.g
引用计数为0,因此立即进行垃圾回收并且python可以自由使用其他任何事物的记忆位置。
值得注意的是,行为不仅限于方法,而且它只是一个cpython实现细节,而不是你应该依赖的东西:
In [67]: id([]), id([])
Out[67]: (139746946179848, 139746946179848)
In [73]: id(tuple()),id([]),id([])
Out[73]: (139747414818888, 139746946217544, 139746946217544)
In [74]: id([]),id([]),id([])
Out[74]: (139746946182024, 139746946182024, 139746946182024)
In [75]: id([]),id(tuple()),id([])
Out[75]: (139746946186888, 139747414818888, 139746946186888)
In [76]: id(tuple()),id([]),id(tuple())
Out[76]: (139747414818888, 139746946217736, 139747414818888)
答案 1 :(得分:4)
Python正在使用相同的内存位置来处理方法a.f
和a.g
,它们是**两个具有非重叠生命周期*的对象,因此{{1 }返回两者的相同标识。请参阅下面的更详细解释。
来自is operator的文档:
运算符是和不是对象标识的测试:x是y是真的 当且仅当x和y是同一个对象时。
来自id
的文档返回对象的“标识”。这是一个整数(或长整数) 整数)保证对于该对象是唯一的和常量的 在其一生中。 具有非重叠生命期的两个对象可以 具有相同的id()值。
<强>说明强>:
每当您通过id
或class.name
查找方法时,方法对象都会创建为a-new。 Python每次都使用descriptor protocol将函数包装在方法对象中。
因此,当您查找instance.name
或id(a.f)
时,会创建一个新的方法对象。
id(a.g)
的id进行grubbing时,会在内存中创建它的副本。此内存位置由a.f
返回。id
的ID后,会在同一内存地址创建一份副本,您可以使用a.g
再次检索该地址。祝你好运!
答案 2 :(得分:2)
答案 3 :(得分:2)
运算符is
检查对象标识,而不是值。在这种情况下,您有两个单独的函数(对象);因此他们有不同的身份。
关于以下部分:
>>> id(a.f) == id(a.g)
True
由于 Python在运行时创建对象,Python第一次尝试获取a.f
的ID时,a.g
尚未定义并基于在Python wiki上两个具有非重叠生命周期的对象可能具有相同的id()值。
因此,在这种情况下,具有非重叠生命周期的对象a.f
和a.g
具有相同的ID。
返回对象的“标识”。这是一个整数(或长整数) 整数)保证对于该对象是唯一的和常量的 在其一生中。两个具有非重叠寿命的对象可以 具有相同的id()值。
关于is
运算符的一些额外说明:
正如我在前面提到的行中所说的,is
运算符将检查对象的身份,并且将在运行时创建Python中的对象。但是对于像整数和字符串这样的小类型来说,情况并非如此,因为它们是单例而不是Python对象。因此,它们将像C类型一样立即位于内存中。
为了更好地演示,您可以看到以下示例:
>>> 100 is 10*10
True
>>>
>>> 1000 is 10*100
False
>>>
对于字符串:
>>> 'aaaa'*5 is 'a'*20
True
>>> 'aaaa'*50 is 'a'*200
False