看看这个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。对于字节和bytearray也是如此。但是对于其他所有类型(如果用作键,可以使用,如tuple或frozenset,还有许多其他类型,如果仅用作值),则会打印True。
如果d是包含x的任何其他容器(set,list,tuple ...),则打印为True。只有dict,它打印False。此外,如果x是键或值,或者如上所述,两者都无关紧要。
我认为在Python中每个对象都是一个引用(而不是Java,它有原始类型,或者Ruby,它是值类型小的int),但是现在看起来str和int是有点原始类型,不是引用。但另一方面,为什么只能在dicts?
我也知道在CPython中缓存了从-5到256的整数(并且可以实现小字符串),因此不重新计算它们是有意义的,因为它们永远不会被删除,但这适用于任何整数(我试过了,比那个范围大得多。
有人知道这里发生了什么吗?
---更新---
Curiouser和curiouser ......似乎是datetime。{datetime,date,time}类具有相同的“未引用”行为。现在,我知道一个那些,AnyStr和Number的共同点:它们的哈希值是随机会话盐。但是这并没有使任何意义,因为即使这些行为仅仅是值而不是键,也会观察到行为。价值观并没有散播。或者是他们?
答案 0 :(得分:4)
来自gcmodule.c
:
某些类型的容器无法参与参考周期,并且 所以不需要被垃圾收集器跟踪。解开这些 对象降低了垃圾收集的成本。但是,确定 哪些对象可能未被跟踪不是免费的,成本必须是 权衡了垃圾收集的好处。
...
仅包含不可变对象的词典也不需要 跟踪。字典在创建时未跟踪。如果是被跟踪的项目 插入字典(作为键或值),字典 跟踪。在完整的垃圾收集(所有代)中, 收藏家将不会删除任何内容不是的词典 跟踪。
基本上,由于Python中的对象是引用计数,因此垃圾收集器的目标是打破引用周期,当最后一个引用消失时,其他未引用的对象将被销毁。为了优化,垃圾收集器不会跟踪某些根本无法参与参考周期的对象。
因此,字符串被引用。但是,垃圾收集器根本不对这些字典感兴趣,因此gc.get_referrers()
不会列出它们。