了解python引用计数以便调试c扩展
我正在编写一个c扩展,并想在pytest中测试它 我测试的部分内容是对象上的引用计数是否正确。因此,我用纯python构建了一个小测试,这让我感到困惑 从Ipython我得到:了解python引用计数以便调试c扩展,python,python-3.x,pytest,reference-counting,python-c-extension,Python,Python 3.x,Pytest,Reference Counting,Python C Extension,我正在编写一个c扩展,并想在pytest中测试它 我测试的部分内容是对象上的引用计数是否正确。因此,我用纯python构建了一个小测试,这让我感到困惑 从Ipython我得到: In [1]: x = 153513514215 In [2]: import sys In [3]: sys.getrefcount(x) Out[3]: 2 所以,一切都很好,一份来自作业,一份来自来电者 但是,下面的脚本(stackoverflow_test.py)给出了以下结果 import sys de
In [1]: x = 153513514215
In [2]: import sys
In [3]: sys.getrefcount(x)
Out[3]: 2
所以,一切都很好,一份来自作业,一份来自来电者
但是,下面的脚本(stackoverflow_test.py)给出了以下结果
import sys
def test_ref_count_int():
x = 677461248192962146784178
assert sys.getrefcount(x) == 2
def test_ref_count_str():
y = 'very long and probbably very unique string'
assert sys.getrefcount(y) == 2
def normal_te_st():
x = 222677461248192962146784178
y = '!!!!very long and probbably unique string!!!!'
print ('x refcount = {}'.format(sys.getrefcount(x)))
print ('y refcount = {}'.format(sys.getrefcount(y)))
if __name__ == '__main__':
normal_te_st()
当我将其作为普通python脚本运行时
$ python3 stackoverflow_test.py
x refcount = 4
y refcount = 4
为什么是4而不是2
当我用pytest运行它时
$ python3 -m pytest stackoverflow_test.py
=================== test session starts ===================
platform linux -- Python 3.4.3, pytest-3.0.7, py-1.4.33, pluggy-0.4.0
rootdir: /opt/projects/0001_Intomics/00005_TextMining/jcr/textmining/tests, inifile:
collected 2 items
stackoverflow_test.py FF
======================== FAILURES =========================
___________________ test_ref_count_int ____________________
def test_ref_count_int():
x = 677461248192962146784178
> assert sys.getrefcount(x) == 2
E assert 3 == 2
E + where 3 = <built-in function getrefcount>(677461248192962146784178)
E + where <built-in function getrefcount> = sys.getrefcount
stackoverflow_test.py:7: AssertionError
___________________ test_ref_count_str ____________________
def test_ref_count_str():
y = 'very long and probbably very unique string'
> assert sys.getrefcount(y) == 2
E AssertionError: assert 3 == 2
E + where 3 = <built-in function getrefcount>('very long and probbably very unique string')
E + where <built-in function getrefcount> = sys.getrefcount
stackoverflow_test.py:11: AssertionError
$python3-m pytest stackoverflow\u test.py
=========================测试会话开始===================
平台linux——Python 3.4.3、pytest-3.0.7、py-1.4.33、pluggy-0.4.0
rootdir:/opt/projects/0001_Intomics/00005_TextMining/jcr/TextMining/tests,文件:
收集2项
stackoverflow_test.py FF
=====================================故障=========================
___________________测试参考计数____________________
def test_ref_count_int():
x=677461248192962146784178
>断言sys.getrefcount(x)==2
E断言3==2
E+式中3=(677461248192962146784178)
E+where=sys.getrefcount
stackoverflow_test.py:7:AssertionError
___________________测试参考计数____________________
def test_ref_count_str():
y='非常长且可能非常独特的字符串'
>断言sys.getrefcount(y)==2
断言错误:断言3==2
E+式中3=(“非常长且可能非常独特的字符串”)
E+where=sys.getrefcount
stackoverflow_test.py:11:AssertionError
为什么是3而不是2
问题:为什么会这样
- python=4个引用计数
- pytest=3个参考计数
- ipython会话=2个参考计数
我希望它在所有3种情况下都像ipython一样,有人能解释发生了什么,并给我一些提示如何最好地测试我正在创建的对象。代码中的文字存储在代码对象中。字节码堆栈是另一个参考:
>>> import dis
>>> def normal_te_st():
... x = 222677461248192962146784178
... y = '!!!!very long and probbably unique string!!!!'
... print ('x refcount = {}'.format(sys.getrefcount(x)))
... print ('y refcount = {}'.format(sys.getrefcount(y)))
...
>>> normal_te_st.__code__.co_consts
(None, 222677461248192962146784178, '!!!!very long and probbably unique string!!!!', 'x refcount = {}', 'y refcount = {}')
>>> dis.dis(normal_te_st)
2 0 LOAD_CONST 1 (222677461248192962146784178)
2 STORE_FAST 0 (x)
3 4 LOAD_CONST 2 ('!!!!very long and probbably unique string!!!!')
6 STORE_FAST 1 (y)
4 8 LOAD_GLOBAL 0 (print)
10 LOAD_CONST 3 ('x refcount = {}')
12 LOAD_ATTR 1 (format)
14 LOAD_GLOBAL 2 (sys)
16 LOAD_ATTR 3 (getrefcount)
18 LOAD_FAST 0 (x)
20 CALL_FUNCTION 1
22 CALL_FUNCTION 1
24 CALL_FUNCTION 1
26 POP_TOP
5 28 LOAD_GLOBAL 0 (print)
30 LOAD_CONST 4 ('y refcount = {}')
32 LOAD_ATTR 1 (format)
34 LOAD_GLOBAL 2 (sys)
36 LOAD_ATTR 3 (getrefcount)
38 LOAD_FAST 1 (y)
40 CALL_FUNCTION 1
42 CALL_FUNCTION 1
44 CALL_FUNCTION 1
46 POP_TOP
48 LOAD_CONST 0 (None)
50 RETURN_VALUE
LOAD_CONST
opcode从附加到code对象的co_CONST
元组加载对象;该元组是一个引用STORE_FAST
然后将其放入局部变量,这是第二个引用
然后是LOAD\u FAST
opcode,它从本地存储中获取一个名称并将其放在堆栈上
最后但并非最不重要的一点是,将该值传递给sys.getrefcount()
调用
如果你想了解什么引用了你的对象,你可能想看看;此函数在调用时排除自身和堆栈,因此您可以在心里添加+2:
>>> import gc
>>> def gc_demo():
... x = 222677461248192962146784178
... print(gc.get_referrers(x))
...
>>> gc_demo()
[(None, 222677461248192962146784178), <frame object at 0x106a25a98>]
导入gc
>>>def gc_demo():
... x=22267461248192962146784178
... 打印(gc.get_参考(x))
...
>>>gc_demo()
[(无,22267461248192962146784178),]
打印2个对象;co_consts
元组和当前调用帧(对于局部变量)
py.test
执行一些附加操作,因此参考计数再次不同
您可能还想用C或C++文档来阅读扩展Python,其中的C API参考手册,最后但并非最不相同的,要学习如何创建一个Python构建,帮助您详细跟踪引用计数。
决不应依赖于对对象的特定数量的引用。我可以通过访问函数对象来为对象添加更多引用,例如(
foo=normal\u te\u st.\uuuu code\uuuu.co\u conts[1]
甚至在运行函数之前都会增加引用计数)。需要增加引用计数的是一个实现细节。请确保您自己的代码正确处理引用。谢谢您的引用,我需要仔细考虑一下,但这个答案正是我所希望的。。。