【发布时间】:2015-09-16 20:32:38
【问题描述】:
看看这段 Python 代码:
from gc import get_referrers as refs
x = 'x'
d = {x:x}
print(d in refs(x))
它打印 False。这本身就很奇怪,但当您考虑以下情况时会变得更加奇怪:
如果 x 是数字(int、float、complex、Fraction、Decimal)而不是字符串,它仍然会打印 False。对于字节和字节数组也是如此。但是对于其他所有类型(如果用作键,则可散列,例如 tuple 或 freezeset - 但还有许多其他类型,如果仅用作值),它会打印 True。
如果 d 是包含 x 的任何其他容器(集合、列表、元组...),则打印 True。仅对于 dict,它会打印 False。此外,x 是键还是值,或者同上,两者都没有关系。
我认为在 Python 中每个对象都是一个引用(与具有原始类型的 Java 或 Ruby 的值类型小整数相反),但现在似乎 str 和 int 有点原始类型,而不是参考。但另一方面,为什么只在 dicts 中??
我也知道从 -5 到 256 的整数被缓存在 CPython 中(并且可以保留小字符串),所以不要引用它们是有意义的,因为它们无论如何都不会被删除,但这适用于任何整数(和长字符串)我试过了,比那个范围大得多。
有人知道这里发生了什么吗?
--- 更新 ---
好奇者和好奇者......似乎 datetime.{datetime, date, time} 类具有相同的“未引用”行为。现在,我知道 AnyStr 和 Number 有一个共同点:它们的哈希值是使用每个会话的盐随机化的。但这没有任何意义,因为即使这些只是dicts中的值,而不是键,也会观察到这种行为。并且值没有被散列。还是他们?
【问题讨论】:
-
有趣的是,引用计数 (
sys.getrefcount) 在分配字典后确实增加了 2。但是get_referrers甚至会在键和值不同时返回 False(其中一个是 x),因此它似乎不能防止循环引用。 -
是的,这是我的假设之一,但正如你所说,它不成立,因为 x 不必既是键又是值。而其他容器(列表)可以有循环引用,它们不会表现出这种行为。
-
顺便说一句,Python 2.7 和 3.4 中的症状相似,引用计数的总数略有不同,这是意料之中的。
标签: python string dictionary reference cpython