了解python引用计数以便调试cextensions

2024-09-30 06:15:10 发布

您现在位置:Python中文网/ 问答频道 /正文

我想用pyc来测试扩展。在

我测试的部分内容是对象的引用计数是否正确。因此,我用纯python构建了一个小测试,这让我很困惑。。。在

从Ipython我得到:

In [1]: x = 153513514215

In [2]: import sys

In [3]: sys.getrefcount(x)
Out[3]: 2

所以一切都很好,一个来自任务的参考,一个来自呼叫者。在

但是下面的脚本(stackoverflow_测试.py)给出了以下结果

^{pr2}$

当我把它作为一个普通的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

为什么是3而不是2?在

问题:为什么会这样

  • python=4个引用计数
  • pytest=3参考计数
  • ipython session=2个引用计数

我希望它在所有3种情况下都像ipython一样,有人能解释一下发生了什么,并给我一些提示,如何最好地测试我正在创建的对象。在


Tags: inpytestrefpytestcountsysfunction
1条回答
网友
1楼 · 发布于 2024-09-30 06:15:10

代码中的文字存储在代码对象中。字节码堆栈是另一个引用:

>>> 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操作码从附加到代码对象的co_consts元组加载对象;该元组是一个引用。STORE_FAST然后将其放入局部变量中,这是第二个引用。在

然后是LOAD_FAST操作码,它从本地存储中取一个名称并将其放入堆栈again incrementing the reference count。在

最后但并非最不重要的是,将该值传递给sys.getrefcount()调用。在

如果您想了解引用对象的内容,可以查看^{};此函数在调用时排除自身和堆栈,因此您可以在心里添加+2:

^{pr2}$

它打印两个对象,co_consts元组和当前调用帧(对于局部变量)。在

py.test执行一些额外的import-time magic,这rewrites ^{} statements,结果引用计数又不同了。在

您可能还想阅读扩展的python的C或C++ +eEM>文档,c++ API手册Objects, Types and Reference Counts section,最后但不是最不相同的Debugging Builds section,来学习如何创建一个Python构建,帮助您详细地跟踪引用计数。在

你不应该依赖于对一个对象的特定数量的引用。我可以通过访问function对象简单地添加对对象的更多引用,例如(foo = normal_te_st.__code__.co_conts[1]甚至在运行函数之前都会增加引用计数)。真正需要增加引用计数的是一个实现细节。只需确保您自己的代码正确处理引用即可。在

相关问题 更多 >

    热门问题