[C] Py_BuildValue und return

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
Antworten
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Hallo.
Py_BuildValue erstellt ja eine neue Referenz auf ein Objekt. Entsteht hiermit nicht zwangsweise ein Memory-Leak?

http://www.python-forum.de/pastebin.php?mode=view&s=19

Es gibt für mich ja keine Möglichkeit mehr, den Reference Count zu verändern :K

Gruß
Grüßle.
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Hi.
Eine andere Frage auch bezüglich der Memory Leaks.
Übernimmt PyList_Append die Referenz, oder nicht?

Gruß
Grüßle.
Benutzeravatar
HerrHagen
User
Beiträge: 430
Registriert: Freitag 6. Juni 2008, 19:07

1.Frage: Ich bin kein Experte auf diesem Gebiet, aber: Du erzeugst ja ein neues Objekt, dieses erhält sogleich eine Referenz welche dann über return aus der Funktion übergeben wird. Das Löschen des Objektes und das Dereferenzieren geschieht ja dann ausserhalb. In diesem Fall:

Code: Alles auswählen

def test():
    a = c_func()
    return
Wird die Referenz des in der Funktion erzeugten Objektes direkt an a weitergereicht. Wenn die Funktion jedoch verlassen wird wird es vernichtet. Ich seh also kein Speicherleck.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Chrisber, die Routine, die dann das `PyObject` bekommt, muss sich drum kümmern, dass das Objekt `Py_DECREF`d wird. So einfach ist das :-)
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

HerrHagen hat geschrieben:Wenn die Funktion jedoch verlassen wird wird es vernichtet.
Jau, aber nur, wenn der aufrufende Funktion nicht als Extension geschrieben wird -- da muss man das manuell machen.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Um die andere Frage zu beantworten, hier ein Auszug aus "Objects/listobject.c":

Code: Alles auswählen

int
PyList_Append(PyObject *op, PyObject *newitem)
{
    if (PyList_Check(op) && (newitem != NULL))
        return app1((PyListObject *)op, newitem);
    PyErr_BadInternalCall();
    return -1;
}
und `app1` sieht so aus:

Code: Alles auswählen

static int
app1(PyListObject *self, PyObject *v)
{
    [snip]

    Py_INCREF(v);
    PyList_SET_ITEM(self, n, v);
    return 0;
}
Antwort: Es erhöht den Referenzzähler des übergebenen Objektes. Genau so verhält es sich dann, wenn die Liste entfernt wird bzw. das Objekt aus der Liste: Der Referenzzähler wird vermindert.
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Okay. Danke an alle :)

Gruß
Grüßle.
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Würde das ganze gerne noch einmal nach oben holen. Danke!
Chrisber hat geschrieben:Py_BuildValue erstellt ja eine neue Referenz auf ein Objekt. Entsteht hiermit nicht zwangsweise ein Memory-Leak?

http://www.python-forum.de/pastebin.php?mode=view&s=19

Es gibt für mich ja keine Möglichkeit mehr, den Reference Count zu verändern :K
Edit: Wenn ich Py_True / Py_False returne (was ja offensichtlich funktioniert) - wie sieht es da mit dem Reference Count aus? Kann dazu nichts finden.
Grüßle.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Eigentlich müsstest du den Referenzcounter für `True` und `False` und `None` wie jeden anderen Referenzcounter behandeln -- aber es wird wohl niemals geschehen, dass `True` oder `False` oder `None` dealloziert werden. Es gibt aber zum Beispiel für das Zurückgeben von `None` extra das Makro `Py_RETURN_NONE`, also sollte es auch nicht besonders schwer sein, den Referenzcounter richtig zu managen.
theliquidwave
User
Beiträge: 221
Registriert: Sonntag 1. Juni 2008, 09:08

Hi.
Danke, wusste gar nicht dass es sowas gibt.
Wenn man sich den Makro genauer anschaut, sieht man:

Code: Alles auswählen

/* Macro for returning Py_None from a function */
#define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None
Wenn man das schlussfolgert sollte sich auch meine Frage beantworten: Python macht den Reference Count automatisch um einen niedriger nach einem "return", oder? Sonst hätte die API an dieser Stelle ja selbst einen Leck.

Edit:

Code: Alles auswählen

/* Macros for returning Py_True or Py_False, respectively */
#define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True
#define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False
:)

Edit2:
// Don't forget to apply Py_INCREF() when returning this value!!!
Gruß
Grüßle.
Antworten