2012-10-25 11 views
5

Sto solo cercando di capire come gestire i conteggi di riferimento quando si utilizza l'API Python C.API Python C, conteggio di riferimento elevato per nuovo oggetto

voglio chiamare una funzione Python in C++, in questo modo:

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; 
codice

Il Python in pythonScript.py è molto semplice:

def run(): 
    return 1 

La documentazione di "PyObject_CallFunctionObjArgs" afferma che ottieni un nuovo riferimento come valore di ritorno. Quindi mi aspetterei "scriptResult" per avere un conteggio di riferimento del 1. Tuttavia l'output è:

scriptResult != null 
print reference count: 72 

Inoltre mi si aspetterebbe una perdita di memoria se vorrei farlo in un ciclo senza diminuire il conteggio dei riferimenti. Tuttavia questo sembra non accadere.

Qualcuno potrebbe aiutarmi a capire?

Cordiali saluti!

+0

Una domanda successiva: grazie a @ecatmur e @KayZhu ora capisco perché non ci sono perdite di memoria. Tuttavia, se eseguo questo codice in un lungo loop, il mio sistema operativo completo si schiaccia comunque. Il numero di riferimenti a '1' sta aumentando, ogni iterazione, ma non vedo perché questo dovrebbe causare un errore di sistema. – user1774143

+0

Sei in loop fino a che 'ob_refcnt' torna indietro a 0? Il conteggio di riferimento di 1 fluttua molto. Quando ci si riprende dopo lo 0, le normali operazioni potrebbero rendere "Py_DECREF" a 0 e far sì che 'int' 1 venga deallocato, seguito rapidamente da un segfault. Provalo con un 'int' internamente meno comune come 13. – eryksun

+0

Almeno, non ero nemmeno in loop fino a 'sys.maxint' (che è '9223372036854775807' sul mio sistema). Oggi sembra che non riesca a riprodurre l'errore e credo che dovrei smettere di provare a scattare il mio desktop funzionante. Grazie per l'aiuto! – user1774143

risposta

2

ecatmur è corretto, i numeri e le stringhe sono internati in Python, quindi invece puoi provare con un semplice oggetto object().

Un semplice demo con gc:

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 

Un po 'di più: quando CPython crea un nuovo oggetto, che definisce un C macro _Py_NewReference per inizializzare il conteggio dei riferimenti a 1. Poi usa Py_INCREF(op) e Py_DECREF(op) per aumentare e diminuire il conteggio dei riferimenti.

+0

Grazie! Comunque se sostituisco s con un numero come 123456.7565 ho ancora 1 referrer. Se lo faccio nel mio codice Python chiamato dal mio codice C++ ottengo ancora 2. Qualche idea del perché questo potrebbe essere? – user1774143

+1

@ user1774143: restituisce '123456.7565' dalla funzione' run() '? In tal caso, il secondo riferimento è la tupla di costanti dell'oggetto codice: 'run .__ code __. Co_consts'. – eryksun

+0

Ah capisco. In questa tupla sono memorizzate tutte le costanti all'interno del metodo, quindi qui ho un riferimento aggiuntivo. Grazie! – user1774143

4

La confusione è che piccoli interi (anche True, False, None, stringhe singolo carattere, etc.) sono internati ("is" operator behaves unexpectedly with integers), il che significa che ovunque siano utilizzati o ottenuti in un programma runtime proverà di utilizzare la stessa istanza oggetto:

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

Ciò significa che quando si scrive return 1, stai tornando un'istanza int oggetto già esistente con (come avete visto) un considerevole numero di riferimento. Poiché la stessa istanza viene utilizzata altrove, non riuscendo a dereferenziare non si verificherà una perdita di memoria.

Se si modifica lo script in return 1001 o return object(), verrà visualizzato un conteggio iniziale di 1 e una perdita di memoria.

+0

Grazie per la rapida risposta, non sapevo nulla della piccola cosa intera! Tuttavia, se cambio il valore di ritorno a 1001, o qualche altro valore divertente, ottengo comunque un conteggio di riferimento di 2 invece di 1. – user1774143

+0

okio vedo ora che per restituire oggetto() il conteggio di riferimento è in effetti 1! – user1774143