Python dicts未引用的字符串?

Python dicts未引用的字符串?,python,string,dictionary,reference,cpython,Python,String,Dictionary,Reference,Cpython,请看以下Python代码: from gc import get_referrers as refs x = 'x' d = {x:x} print(d in refs(x)) 它印的是假的。这本身就很奇怪,但是当你考虑以下情况时变得更加奇怪: 如果x是一个数字(int、float、复数、分数、十进制)而不是一个字符串,它仍然打印False。对于字节和字节数组也是如此。但对于其他每一种类型(如果用作键,如tuple或frozenset,则为hashable,但如果仅用作值,则为许多其他类型)

请看以下Python代码:

from gc import get_referrers as refs
x = 'x'
d = {x:x}
print(d in refs(x))
它印的是假的。这本身就很奇怪,但是当你考虑以下情况时变得更加奇怪:

  • 如果x是一个数字(int、float、复数、分数、十进制)而不是一个字符串,它仍然打印False。对于字节和字节数组也是如此。但对于其他每一种类型(如果用作键,如tuple或frozenset,则为hashable,但如果仅用作值,则为许多其他类型),它都打印为True

  • 如果d是包含x的任何其他容器(集合、列表、元组…),它将打印True。只有口述,它打印的是假的。另外,x是键还是值,或者如上所述,两者都是,这并不重要

我认为在Python中,每个对象都是一个引用(与Java相反,Java有基本类型,或者Ruby,Ruby的值类型是小int),但现在看来str和int是一种基本类型,没有被引用。但另一方面,为什么只在字典里

我还知道,从-5到256的整数被缓存在CPython中(小字符串可以插入),所以不重新计算它们是有意义的,因为它们无论如何都不会被删除,但这适用于我尝试过的任何整数(和长字符串),比这个范围大得多

有人知道这里发生了什么吗

---更新---


好奇者和好奇者。。。似乎是datetime。{datetime,date,time}类具有相同的“未引用”行为。现在,我知道AnyStr和Number有一个共同点:它们的散列是随机的,每会话一次。但这没有任何意义,因为即使这些只是dict中的值,而不是键,也会观察到行为。和值不进行散列。还是它们?

来自
gcmodule.c

某些类型的容器不能参与引用循环,以及 因此不需要由垃圾收集器跟踪。解开这些 对象降低了垃圾收集的成本。然而,决定 哪些对象可能未被跟踪不是免费的,而且成本必须是合理的 权衡垃圾收集的好处

只包含不可变对象的字典也不需要修改 跟踪。词典创建时不受跟踪。如果跟踪的项目是 插入字典(作为键或值),字典 被跟踪。在完整垃圾收集期间(所有代), 收集器将取消跟踪内容不正确的任何词典 跟踪

基本上,由于Python中的对象是引用计数,垃圾收集器的目标是打破引用周期,当最后一个引用消失时,其他未引用的对象将被销毁。为了进行优化,垃圾收集器不跟踪某些对象,因为这些对象永远无法参与引用循环


因此,字符串被引用。但是,垃圾收集器对这些字典根本不感兴趣,因此
gc.get\u referers()
不会列出它们。

有趣的是,分配字典后引用计数(
sys.getrefcount
)会增加2。但是如果键和值不同(其中一个是x),那么
get_referers
甚至返回False,因此它似乎不能防止循环引用。是的,这是我的假设之一,但正如您所说,它不成立,因为x不必同时是键和值。和其他容器(列表)可以有循环引用,它们不会表现出这种行为。顺便说一句,Python 2.7和3.4中的症状类似,引用计数的总数明显不同,这是意料之中的。刚刚发现,在源代码中,你也比我快。有趣的是,如果该值是不可变的(键不能是),那么该值将出现在
get\u referers()
中,但该键从未出现过,这可能会与引号相矛盾(尽管它说字典将被跟踪,而不是键)。删除最后一条注释。如果(我猜)缓存了密钥,则不会跟踪它。将
x
更改为“Slartibartfast”,将值更改为
[1,2,3,4]
,在
gc.get\u referers()
中提供键和值作为跟踪。很明显,这里有一些魔力。正如拉里·沃尔(Larry Wall)曾经说过的那样,似乎把源头变大对灵魂真的有好处。:-)谢谢但我还是不明白为什么只有听写而没有录音。。。就好像在Python中集合实际上是二级对象一样。:-)