Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/redis/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Python中新创建的变量的ref计数为4?_Python - Fatal编程技术网

为什么Python中新创建的变量的ref计数为4?

为什么Python中新创建的变量的ref计数为4?,python,Python,我一直在为同事做一个演示,解释GIL背后的基本行为和推理,发现了一些我无法解释的东西,同时对参考计数进行了快速解释。新声明的变量似乎有四个引用,而不是我所期望的引用。例如,以下代码: the_var = 'Hello World!' print('Var created: {} references'.format(sys.getrefcount(the_var))) 此输出的结果如下: Var created: 4 references 如果我使用了大于100的整数( []/Cord>,而

我一直在为同事做一个演示,解释GIL背后的基本行为和推理,发现了一些我无法解释的东西,同时对参考计数进行了快速解释。新声明的变量似乎有四个引用,而不是我所期望的引用。例如,以下代码:

the_var = 'Hello World!'
print('Var created: {} references'.format(sys.getrefcount(the_var)))
此输出的结果如下:

Var created: 4 references
如果我使用了大于100的整数(<100是预先创建的,并且具有更大的ref计数)或float,并且如果我在函数范围或循环中声明了变量,那么我验证了输出是相同的。结果是一样的。2.7.11和3.5.1中的行为似乎也相同

我试图调试sys.getrefcount以查看它是否正在创建其他引用,但无法进入该函数(我假设它是直接向下到C层的)


我知道当我演讲的时候,我至少会有一个问题要问,而且我对结果还是很困惑。有人能给我解释一下这种行为吗?

有几种情况会产生不同的引用计数。最简单的是REPL控制台:

>>> import sys
>>> the_var = 'Hello World!'
>>> print(sys.getrefcount(the_var))
2
>>> import gc
>>> the_var = 'Hello World!'
>>> gc.get_referrers(the_var)
[{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'the_var': 'Hello 
World!', 'gc': <module 'gc' (built-in)>, '__name__': '__main__', '__doc__': None}]
理解这个结果非常简单-本地堆栈中有一个引用,
sys.getrefcount()
函数的另一个临时/本地引用(即使是警告-
返回的计数通常比您预期的要高一个)。但当您将其作为独立脚本运行时:

import sys

the_var = 'Hello World!'
print(sys.getrefcount(the_var))
# 4
import gc
import pprint

the_var = 'Hello World!'
pprint.pprint(gc.get_referrers(the_var))
正如您所注意到的,您会得到一个
4
。那么是什么原因呢?好吧,让我们调查一下。。。垃圾收集器有一个非常有用的接口,
gc
模块,因此如果我们在REPL控制台中运行它:

>>> import sys
>>> the_var = 'Hello World!'
>>> print(sys.getrefcount(the_var))
2
>>> import gc
>>> the_var = 'Hello World!'
>>> gc.get_referrers(the_var)
[{'__builtins__': <module '__builtin__' (built-in)>, '__package__': None, 'the_var': 'Hello 
World!', 'gc': <module 'gc' (built-in)>, '__name__': '__main__', '__doc__': None}]
这将输出(YMMV,基于您的Python版本):

你会得到:

[{'__builtins__': <module '__builtin__' (built-in)>,
  '__doc__': None,
  '__file__': 'test.py',
  '__name__': '__main__',
  '__package__': None,
  'gc': <module 'gc' (built-in)>,
  'pprint': <module 'pprint' from 'D:\Dev\Python\Py27-64\lib\pprint.pyc'>,
  'the_var': 'Hello World!'}]
就像在REPL控制台中一样,正如预期的那样。由于窥视孔优化而产生的额外引用发生在适当的位置,可以在计算引用之前通过强制垃圾收集(
gc.collect()
)立即丢弃


但是,编译期间创建的字符串列表在整个文件被解析和编译之前无法释放,这就是为什么如果您将脚本导入到另一个脚本中,然后从中计算对
the_var
的引用,您会得到
3
,而不是
4
,就在您认为它不会再让您困惑的时候;)

您是直接在python还是ipython shell中键入它?还是将其作为独立脚本运行?我在纯python shell中得到了2个引用。当我在2.7.13中运行代码时,我得到了
Var创建:2个引用
。我希望在交互模式下得到2个引用,作为脚本得到3个引用(代码对象常量中的一个额外引用),但不是4个。您可能已经做了一些其他的事情来创建另一个引用。考虑使用一个可变的对象,如代码> []/Cord>,而不是一个不可变的演示。可变对象不能接受大量缓存和不断折叠优化,这些优化可能会对引用计数产生意外影响。据我所知,我是通过PyCharm scratch文件运行的,所以是作为脚本运行的。很可能是不可变的对象元素为我提供了额外的意外引用,因为使用
[]
而不是
'Hello World'
会使我减少到两个引用。我无法重现您看到的输出-
4
,或额外的
gc.get\u referers()
条目。您使用什么来运行此代码?@user2357112-除非在最新的CPython版本中有所更改,否则这应该是标准
python test_script.py
执行的默认行为。当然,如果您将脚本编译为PYC并运行它,您将不会得到
4
——所有额外的引用都是由于编译/优化过程。当我有时间的时候,我会看一看最新的CPython源代码,但我不认为这有什么明显的变化。在3.6上尝试它(因为我忘记了Ideone是在3.5上的)会复制
4
和列表,但不会复制
(-1,None,'Hello World!'
tuple.3.6.0,也就是说。这可能取决于微版本的版本差异。@user2357112-如果垃圾收集器启动(或您显式调用它),它将不会显示(尽管
sys.getrefcount()
仍将对其进行计数)。也许他们用窥视孔优化器改变了一些东西,但我不知道。我会看看是否能抽出一些时间来调查接下来几天的最新变化,并相应地报告。
import sys

exec(compile("the_var = 'Hello World!'", "<string>", "exec"))
print(sys.getrefcount(the_var))
# 2