为什么同一个对象的不同方法具有相同的`id`?

Tim*_*mur 16 python

我以为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
Run Code Online (Sandbox Code Playgroud)

Pad*_*ham 15

Python重用相同的内存位置,因为你没有对对象进行其他引用,一旦id(a.f)被评估有更多对该对象的引用,所以它是gc'd然后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
Run Code Online (Sandbox Code Playgroud)

实际上你真的只需要存储对f的引用来查看与上面相同的行为.

In [201]: f = a.f

In [202]: id(f) == id(a.g)
Out[202]: False
Run Code Online (Sandbox Code Playgroud)

您可以使用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)
Run Code Online (Sandbox Code Playgroud)

您为ag看到1的唯一原因是因为返回的计数通常比您预期的高一个,因为它包含(临时)引用作为getrefcount()的参数. 它类似于你自己的例子,在评估方法之后,你仍然会引用f,a.grefcount将为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)
Run Code Online (Sandbox Code Playgroud)


And*_*yko 4

a.fPython 方法和正在使用相同的内存a.g位置,它们是**两个具有不重叠生命周期的对象*,因此id为它们返回相同的标识。请参阅下面更详细的解释。

\n\n

来自is 运算符的文档:

\n\n
\n

运算符 is 和 is not 测试对象身份:x is y is true\n 当且仅当 x 和 y 是同一对象时。

\n
\n\n

从文档中可以看出来自 is id

\n\n
\n

返回对象的\xe2\x80\x9cidentity\xe2\x80\x9d。这是一个整数(或 long\n 整数),保证该对象在其生命周期内是唯一且恒定的。具有不重叠生命周期的两个对象可能具有相同的 id() 值。

\n
\n\n

说明

\n\n

class.name每当您通过或查找方法时instance.name,都会创建一个新的方法对象。Python使用描述符协议将函数包装在方法对象中。

\n\n

所以,当你抬头id(a.f)id(a.g)时,会创建一个新的方法对象。

\n\n
    \n
  1. 当您抓取 id 时a.f,会在内存中创建它的副本。该内存位置由以下命令返回id
  2. \n
  3. 由于没有对新创建的方法的引用,因此它被 GC 回收(现在内存地址再次可用)。
  4. \n
  5. 获取 id of 后a.g,会在同一内存地址创建它的副本,您可以使用以下命令检索该副本id再次检索该地址。
  6. \n
  7. 你说的是实话id 的比较。
  8. \n
\n\n

祝你好运!

\n


归档时间:

查看次数:

1888 次

最近记录:

7 年,3 月 前