Py_INCREF

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Maple99
User
Beiträge: 44
Registriert: Montag 14. September 2009, 18:08

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
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

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.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Maple99
User
Beiträge: 44
Registriert: Montag 14. September 2009, 18:08

Ah okay danke.
Nachdem ich den ganzen Abend nur Segmentation Faults gesehen habe macht das Sinn ;)

Super. 1000 Dank!!
Zap
User
Beiträge: 533
Registriert: Freitag 13. Oktober 2006, 10:56

Also wäre dann doch die richtige Lösung nicht self zurück zu geben sondern None, oder?!
Maple99
User
Beiträge: 44
Registriert: Montag 14. September 2009, 18:08

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
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

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.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Maple99
User
Beiträge: 44
Registriert: Montag 14. September 2009, 18:08

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:
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.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

@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.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
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.
Benutzeravatar
Trundle
User
Beiträge: 591
Registriert: Dienstag 3. Juli 2007, 16:45

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.
"Der Dumme erwartet viel. Der Denkende sagt wenig." ("Herr Keuner" -- Bertolt Brecht)
Maple99
User
Beiträge: 44
Registriert: Montag 14. September 2009, 18:08

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
Antworten