2

私はc-エクステンションを作成しており、pytestでテストしたいと考えています。c-エクステンションをデバッグするためのPythonリファレンスカウントの理解

私がテストしていることの一部は、参照カウントが自分のオブジェクト上で正しいかどうかです。従って、私は私が手Ipythonから私を困惑純粋なPythonで小さなテスト...

を構築:

In [1]: x = 153513514215 

In [2]: import sys 

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

ので、発信者からの割り当てと1からとても良い、1つの参照運賃。私は、通常のPythonスクリプト

$ python3 stackoverflow_test.py 
x refcount = 4 
y refcount = 4 

なぜ4なく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() 

を与えるしかし

?。

私は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 refは
  • pytest = 3 refは
  • セッションipython = 2 refが

をカウントし、私はそれを期待をカウントを数えますすべての3つのケースでipythonのように振舞い、誰が何が起こっているのかを説明し、私が作成しているオブジェクトを最もよくテストするためのヒントをいくつか与えることができます。

答えて

4

コード内のリテラルはコードオブジェクトに格納されます。バイトコードスタックは、別の基準である。

>>> 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タプルからオブジェクトをロードします。そのタプルは1つの参照です。 STORE_FASTはそれをローカル変数に入れます。これは2番目の参照です。

オペコードがLOAD_FASTで、これはローカルストレージから名前をとり、スタックに入れます(again incrementing the reference count)。

最後に、この値をsys.getrefcount()コールに渡します。

オブジェクトを参照する方法については、gc.get_referrers()を参照してください。この関数は呼び出し時に自分自身とスタックを除外しますので、+2を精神的に追加することができます:

>>> import gc 
>>> def gc_demo(): 
...  x = 222677461248192962146784178 
...  print(gc.get_referrers(x)) 
... 
>>> gc_demo() 
[(None, 222677461248192962146784178), <frame object at 0x106a25a98>] 

これは2つのオブジェクトを出力します。 co_constsタプル、および現在の呼び出しフレーム(ローカルの場合)。

py.testimport-time magicrewrites assert statementsとなり、その結果、参照カウントが再び異なっています。

あなたはまた、どのように学ぶために、CまたはC++ドキュメント、C APIリファレンスマニュアル、そして最後のObjects, Types and Reference Counts sectionではなく、少なくとも同じ同じのDebugging Builds sectionでのPythonの拡張Reference Counts sectionを読むことをお勧めしますリファレンスカウントを詳細にトレースするのに役立つPythonビルドを作成します。

オブジェクトに特定の数の参照を頼るべきではありません。関数オブジェクトに到達することで、あなたのオブジェクトへの参照を追加することができます。例えば、(foo = normal_te_st.__code__.co_conts[1]は、関数を実行する前でも参照カウントをインクリメントします)。参照カウントが正確に上昇する必要があるのは実装の詳細です。 自分のコードが参照を正しく処理するようにしてください。

+0

参考に感謝します。私はそれを噛む必要がありますが、この答えは私が望んでいたものでした... – jcr

関連する問題