2012-10-25 16 views
5

Sadece Python C API'sini kullanırken referans sayımlarının nasıl ele alınacağını anlamaya çalışıyorum.Python C API, Yeni Nesne için yüksek referans sayısı

böyle, C++, Python işlevi çağırmak istiyorum:

PyObject* script; 
PyObject* scriptRun; 
PyObject* scriptResult; 

// import module 
script = PyImport_ImportModule("pythonScript"); 
// get function objects 
scriptRun = PyObject_GetAttrString(script, "run"); 
// call function without/empty arguments 
scriptResult = PyObject_CallFunctionObjArgs(scriptRun, NULL); 

if (scriptResult == NULL) 
    cout << "scriptResult = null" << endl; 
else 
    cout << "scriptResult != null" << endl; 

cout << "print reference count: " << scriptResult->ob_refcnt << endl; 
pythonScript.py içinde

Python kodu çok basit:

def run(): 
    return 1 

"PyObject_CallFunctionObjArgs" dokümantasyonu söylüyor Dönüş değeri olarak yeni bir referans alırsınız. Ben başvuru sayısı düşürmeden bir döngü içinde bunu olsaydı

scriptResult != null 
print reference count: 72 

Ayrıca ben bir bellek sızıntısı beklenir: çıkışı Ancak Yani 1. başvuru sayısı olması "scriptResult" beklenebilir. Ancak bu olmayacak gibi görünüyor.

Birisi anlayamama yardımcı olabilir mi?

Saygılarımızla!

+0

Takip eden bir soru: @ecatmur ve @KayZhu'ya teşekkürler Artık neden bellek sızıntısı olmadığını anlıyorum. Yine de bu kodu uzun bir döngüde çalıştırırsam, tamamen os ezerim. '1' referans sayısı artmaktadır, her yineleme, ancak bunun neden bir sistem arızasına neden olacağını görmüyorum. – user1774143

+0

'ob_refcnt' 0'a geri dönene kadar döngü mü yapıyorsun? 1 referans sayısı çok dalgalanır. 0'a yaklaştığınız zaman, normal işlemler Py_DECREF '0'a ve 'int'1'in serbest bırakılmasına neden olur, daha sonra bir segfault tarafından hızlıca takip edilir. Daha az rastlanan bir int ile int (int. 13 – eryksun

+0

) En azından ben 'sys.maxint' (sistemimde '9223372036854775807' olana kadar) bile döngü yapmıyordum. Bugün bu hatayı yeniden üretemiyorum ve sanırım çalışmamdaki masaüstümü vurmaya çalışmam gerekmiyor. Yardım ettiğin için teşekkür ederim! – user1774143

cevap

2

ecatmur doğru, sayı ve dizgiler Python'da barındırılıyor, dolayısıyla basit bir object() nesnesiyle deneyebilirsiniz.

gc ile basit bir demo:

import gc 


def run(): 
    return 1 

s = run() 
print len(gc.get_referrers(s)) # prints a rather big number, 41 in my case 

obj = object() 
print len(gc.get_referrers(obj)) # prints 1 

lst = [obj] 
print len(gc.get_referrers(obj)) # prints 2 

lst = [] 
print len(gc.get_referrers(obj)) # prints 1 again 

Biraz daha: CPython yeni bir nesne oluşturduğunda, bu çağıran bir C 1. Sonra Py_INCREF(op) ve Py_DECREF(op) kullandığı için başvuru sayısı başlatmak için _Py_NewReference makro referans sayısını arttırmak ve azaltmak için.

+0

Teşekkür ederiz! Ancak ben s 123456.7565 gibi bir sayı ile değiştirirsem hala 1 yönlendiren alırım. C++ kodumla çağrılan python kodumda bunu yaparsam hala 2 alırım. Herhangi bir fikri neden bu olabilir? – user1774143

+1

@ user1774143: 'run()' işlevinden '123456.7565' döndürüyor mu? Eğer öyleyse 2. referans kod nesnesinin sabitlerin tuple'ıdır: 'run .__ code __. Co_consts'. – eryksun

+0

Ah Anladım. Bu tupleda, yöntem içindeki tüm sabitler saklanır, böylece burada bir ek referansım olur. Teşekkürler! – user1774143

4

karmaşa (aynı zamanda True, False, None, tek karakter dizgileri, vs.), küçük tam sayılar bir programda kullanılan veya elde edilen her yerde zamanı çalışacaktır, yani enterne ("is" operator behaves unexpectedly with integers) olmasıdır

>>> 1 is 1 
True 
>>> 1 + 1 is 2 
True 
>>> 1000 + 1 is 1001 
False 

Bu return 1 yazarken (gördüğünüz gibi) kullanarak, bir hayli başvuru sayısı mevcut bir int nesne örneğini dönen olduğumuz anlamına gelir: aynı nesne örneğini kullanmak. Aynı örnek başka bir yerde kullanıldığından, bunun yapılmaması bir bellek sızıntısına neden olmaz.

Komut dosyanızı return 1001 veya return object() olarak değiştirirseniz, ilk referans sayısı 1 ve bir bellek sızıntısı görürsünüz.

+0

Hızlı cevap için teşekkürler, küçük tamsayı olayı hakkında bilmiyordum! Ancak, dönüş değerini 1001'e veya başka bir komik değere değiştirirsem, hala 1 yerine 2'lik bir referans sayısı alırım. – user1774143

+0

tamam şimdi geri dönen nesne() için referans sayısının aslında 1 olduğunu görüyorum! – user1774143