Seite 1 von 1

Py_INCREF

Verfasst: Montag 26. Oktober 2009, 21:25
von Maple99
Hi,

ich habe mal kurz eine Frage. Ich habe einen Datentyp/Klasse in C gebaut. Diese hat einen paar Funktionen der Art:

Code: Alles auswählen

static PyObject * append(spam *self, PyObject* args){
    PyObject* mySpam = Spam_New(&SpamType, args, NULL);
    if (Spam_Init((spam*)mySpam,args, NULL)) {
        fprintf(stderr,"Fehler\n");
        return NULL;
    }
    Py_INCREF(self);
    self->next = spam;
    return (PyObject *)self;
}
Es wird also eine Listenstruktur erstellt. Meine Frage bezieht sich jetzt auf das Py_INCREF(self). Wenn ich dies nicht ausführe, dann kommt der GarbageCollector an und führt dealloc aus. Wieso passiert das? Wie kann es sein das der Refcount 0 wird? Self wurde doch vorher schon erstellt und ich kommt mit ihm arbeiten.

Der Aufruf aus dem Interpreter sieht so aus:

a=makeOneElement(3)
a.append(42) <-- hier nach kommt der dealloc wenn ich das Py_INCREF nicht mache ;(

Gruß

Jonny

Verfasst: Montag 26. Oktober 2009, 21:37
von Trundle
Weil der Rückgabewert verworfen wird und der Interpreter dabei eben den Referenzzähler um eins erniedrigt, wenn er den Rückgabewert vom Stack `pop`t.

Verfasst: Montag 26. Oktober 2009, 21:39
von Maple99
Ah okay danke.
Nachdem ich den ganzen Abend nur Segmentation Faults gesehen habe macht das Sinn ;)

Super. 1000 Dank!!

Verfasst: Dienstag 27. Oktober 2009, 04:55
von Zap
Also wäre dann doch die richtige Lösung nicht self zurück zu geben sondern None, oder?!

Verfasst: Dienstag 27. Oktober 2009, 19:48
von Maple99
Was ist denn guter Stil?

Lieber ein Py_INCREF(self) oder return Py_None?? Leider finde ich keine entsprechende Stelle in der Python-Doku. Wäre über eine Meinung sehr dankbar.

Gruß

Jonny

Verfasst: Dienstag 27. Oktober 2009, 19:54
von Trundle
Was man zurückgibt, ändert ja an der grundlegenden Problematik nichts. Wenn man `None` zurückgeben würde, müsste man davon vorher auch den Referenzzähler um eins erhöhen.

Verfasst: Freitag 30. Oktober 2009, 15:55
von Maple99
Hi,

dazu jetzt noch mal kurz eine Frage. Wenn ich self zurückgebe, dann muss ich Py_INCREF(self) durchführen, sodass ich sicherstelle das nachher der Refcount min. 1 ist. Das selbe müsste ich also auch machen für Py_None.
Jetzt habe ich in einer anderen Funktion aus einem Wert meines C-"Objekts" (aus meinem struct) ein Python-Objekt per Py_BuildValue(..) erzeugt und gebe es am Funktionsende zurück. Ich erhalte von Py_BuildValue ja eine neue Referenz. Wenn diese allerdings auch bei der Rückgabe dekrementiert wird, verliere ich die Referenz ja wieder und der gc könnte zuschlagen. Allerdings habe ich es mal getestet und das Ergebnis scheint sogar noch zu leben, wenn das dealloc ausgeführt wurde. Scheinbar brauche ich hier also kein Py_INCREF() Nur warum??
Könnte mir das vielleicht noch mal jemand etwas genauer erklären? :oops:

Verfasst: Freitag 30. Oktober 2009, 18:12
von BlackJack
@Maple99: Du musst das ``Py_INCREF(self)`` nicht ausführen, wenn Du `self` zurückgibst. Das ist gar nicht der Punkt. Du musst das machen, wenn Du auf `self` einen Pointer behältst, den Du später noch benutzen willst (und den Du nicht schon hattest).

Bei dem am Anfang gezeigten Code-Schnippsel, was sowieso falsch ist, weil `spam` hier ein Typ ist, den man nicht zuweisen kann und `mySpam` nicht weiter verwendet wird, muss man kein ``Py_INCREF(self)`` machen. Wenn *da* was nicht funktioniert, dann wurde schon ein `self` mit zu niedrigem Referenzzähler in die Funktion herein gereicht.

Verfasst: Freitag 30. Oktober 2009, 19:09
von Trundle
@BlackJack: Das `self` ist doch völlig unabhängig von `mySpam`. Warum sollte `mySpam` das Reference-Counting von `self` beeinflussen? Auch wenn das mit dem Zuweisen von `spam` natürlich richtig ist. Trotzdem *muss* der Referenzzähler von `self` erhöht werden, wenn `self` zurückgegeben wird.

Und bei `Py_BuildValue()` musst du nicht erhöhen, da deine Funktion selbst ja noch eine Referenz auf den Rückgabewert besitzt. Wenn du das von `Py_BuildValue` erstellte Objekt nicht zurückgibst, musst du es `Py_DECREF`en, gibst du es zurück eben nicht.

Verfasst: Freitag 30. Oktober 2009, 20:46
von BlackJack
@Trundle: `mySpam` hat nichts mit `self` zu tun. Habe ich das behauptet? Ich wollte nur darauf hinweisen, dass das Beispiel a) nicht kompiliert weil `spam` als Typ und als Wert verwendet wird, was kein gültiges C ist, und b) `mySpam` nicht verwendet wird, was wohl nicht beabsichtigt ist, weil sinnlos.

Wieso muss in dem Quelltext von `self` der Referenzzähler erhöht werden? `self` kommt von aussen in die Funktion, die Funktion speichert das nicht irgendwo, wo man später noch heran kommt, und gibt es wieder zurück. IMHO kein Grund den Referenzzähler zu erhöhen. Aus Sicht der Funktion könnte `self` nach dem Rückgeben abgeräumt werden.

Verfasst: Freitag 30. Oktober 2009, 20:58
von Trundle
Das hat sich wegen dem "und `mySpam` nicht weiter verwendet wird, muss man kein ``Py_INCREF(self)`` machen" für mich irgendwie so angehört.

Und der Referenzzähler von `self` muss erhöht werden, weil PyCFunctions immer eine *neue* Referenz zurückgeben.

Beispiel: ``spam.spam()``. Wir nehmen an, `spam` ist die einzige Referenz auf das Objekt, auf das `spam` verweist, womit der Referenzzähler genau 1 ist. Die `spam`-Methode gibt `self` zurück. Da der Rückgabewert nicht gespeichert wird, findet gleich nach dem Methodenaufruf ein `POP_TOP` statt, um den Rückgabewert vom Stack zu nehmen, da er nicht verwendet wird. `POP_TOP` erniedrigt den Referenzzähler jedoch => Referenzzähler ist 0, `spam` verweist aber immer noch auf das Objekt.

Verfasst: Sonntag 1. November 2009, 21:26
von Maple99
Hi,

also auf self->next soll natürlich das neue mySpam zugewiesen werden. Ich hatte den Code noch etwas modifiziert nachdem ich ihn hier eingefügt hatte und da ist mir das wohl durchgerutscht.

Danke euch beiden. Die Erläuterung mit spam.spam() ohne Zuweisung scheint mir sehr plausibel. Danke ;)

Gruß

Jonny