Warum sollte Py_INCREF(Py_None) erforderlich sein, bevor Py_None in C zurückgegeben wird?

Lesezeit: 4 Minuten

Warum sollte Py_INCREF(Py_None) erforderlich sein, bevor Py_None in C wie folgt zurückgegeben wird?

Py_INCREF(Py_None);
return Py_None;

Was passiert, wenn Py_INCREF(Py_None) weggelassen wird?

Benutzer-Avatar
Bakuriu

Fehlt ein Py_INCREF führt zu einer falschen Zählung der Referenzen für Py_Nonewas dazu führen kann, dass der Interpreter die Zuordnung aufhebt Py_None. Seit Py_None wird statisch im zugewiesen Objects/object.c Datei:

PyObject _Py_NoneStruct = {
  _PyObject_EXTRA_INIT
  1, &PyNone_Type
};

Und in Include/object.h Da ist die Definition:

#define Py_None (&_Py_NoneStruct)

Was also passieren wird, ist, dass der Interpreter mit einem schwerwiegenden Fehler abstürzt:

Fatal Python error: deallocating None

Was durch die erzeugt wird none_dealloc Funktion ein Objects/object.c:

/* ARGUSED */
static void
none_dealloc(PyObject* ignore)
{
    /* This should never get called, but we also don't want to SEGV if
     * we accidentally decref None out of existence.
     */
    Py_FatalError("deallocating None");
}

Wie in diesem Kommentar angegeben, wenn NoneType keine eigene Funktion zum Aufheben der Zuordnung hätte, würden Sie einen Segmentierungsfehler erhalten, da a free Der Aufruf würde auf dem Stack erfolgen.

Sie können dies testen, indem Sie das Beispiel in kopieren LernprogrammHinzufügen eines Anrufs zu Py_DECREF(Py_None) in die Noddy_name Funktion, erstellen Sie die Erweiterung und führen Sie eine Schleife aus, die diese Methode aufruft.


Im allgemeinen Fall eine Referenzanzahl von 0 kann auf viele verschiedene Arten dazu führen, dass das Programm fehlschlägt.

Insbesondere kann Python den Speicher, der von den freigegebenen Objekten verwendet wird, wiederverwenden, was bedeutet, dass plötzlich jeder Verweis auf ein Objekt zu einem Verweis auf ein zufälliges Objekt (oder auf einen leeren Speicherort) werden kann, und Sie könnten Dinge wie sehen :

>>> None   #or whatever object that was deallocated
<ARandomObjectYouNeverSawBefore object at ...>

(Das ist mir tatsächlich manchmal passiert, als ich C-Erweiterungen geschrieben habe. Einige Objekte wurden aufgrund fehlender Aufrufe zu zufälligen Zeiten in Nur-Lese-Puffer umgewandelt Py_INCREF).

In anderen Situationen könnten andere Arten von Fehlern ausgelöst werden oder der Interpreter könnte abstürzen oder einen Segfault verursachen.

  • PyNone wird wahrscheinlich statisch zugewiesen (keine Python-Quelle, um es nachzuschlagen), also würden wir versuchen, statisch zugewiesenen Speicher freizugeben, was besonders interessant wäre. Auf jeden Fall kommt nichts Gutes dabei heraus.

    – Voo

    8. März 2013 um 8:05 Uhr

  • @Voo Das dachte ich auch, und es stellt sich heraus, dass es statisch zugewiesen ist (in object.c), und dies führt zu einem schwerwiegenden Fehler. Wie auch immer, meine Argumentation war richtig, außer dass sie für den allgemeineren Fall gilt, dass vergessen wird, ein generisches Objekt zu inkrementieren.

    – Bakuriu

    8. März 2013 um 10:57 Uhr

Benutzer-Avatar
Jared

Py_None ist wirklich nur ein weiteres Python-Objekt, außer ohne Methoden.

Python zählt die Referenzen auf any PyObject*. Es spielt keine Rolle, ob es sich um eine Zeichenfolge, eine ganze Zahl oder None handelt.

Wenn Sie die Referenzanzahl nicht erhöhen, verwirft der Python-Interpreter das Objekt schließlich, nachdem die Referenzanzahl erreicht wurde 0, und denken, dass es keine Zeiger auf das Objekt gibt. Das bedeutet, dass Sie beim nächsten Versuch, etwas mit dem Rückgabewert zu tun, einem Zeiger auf eine Stelle im Speicher folgen werden, die nicht garantiert gehalten wird Py_None (Fehler, seltsamer Wert, Segmentierungsfehler usw.).

Es gibt Alternativen, sich an die Verwendung zu erinnern Py_INCREF(Py_None):

return Py_BuildValue("");

oder

Py_RETURN_NONE;

  • Danke für das Makro.

    – Verrückter Physiker

    18. April 2018 um 17:48 Uhr

  • Führt ein Fehler bei Py_DECREF zu Py_None zu etwas Falschem, abgesehen davon, dass Sie sich selbst das falsche mentale Modell beibringen, dass Sie sich keine Sorgen um die Referenzanzahl von Py_None machen müssen?

    – Matthäus Moisen

    21. Januar 2020 um 19:37 Uhr

  • @MatthewMoisen: Wenn Sie es ein paar Milliarden Mal auf 32-Bit-Python nicht schaffen, kann es zu negativen Werten zurückkehren und schließlich Null erreichen, wenn Sie in die andere Richtung kommen (und ein schlecht getimtes Py_DECREF würde dann versuchen, es zu befreien). Es würde eher viel mehr fehlen DECREFs, um das Problem auf 64-Bit-Python zu treffen, aber es ist technisch möglich.

    – ShadowRanger

    15. Oktober 2021 um 19:01 Uhr

1368610cookie-checkWarum sollte Py_INCREF(Py_None) erforderlich sein, bevor Py_None in C zurückgegeben wird?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy