Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

PEP 670 – Makros in der Python C API in Funktionen umwandeln

Autor:
Erlend Egeberg Aasland <erlend at python.org>, Victor Stinner <vstinner at python.org>
Status:
Final
Typ:
Standards Track
Erstellt:
19-Okt-2021
Python-Version:
3.11
Post-History:
20-Okt-2021, 08-Feb-2022, 22-Feb-2022
Resolution:
Python-Dev thread

Inhaltsverzeichnis

Zusammenfassung

Makros in der C API werden in statische Inline-Funktionen oder reguläre Funktionen umgewandelt. Dies hilft, Makro-Fallstricke in C/C++ zu vermeiden und macht die Funktionen für andere Programmiersprachen nutzbar.

Um Compiler-Warnungen zu vermeiden, werden Funktionsargumente vom Zeigertyp mithilfe zusätzlicher Makros in geeignete Typen gecastet. Der Cast wird in der Limited C API Version 3.11 nicht durchgeführt: Benutzer, die die neue Limited API verwenden, müssen möglicherweise Casts zum exakt erwarteten Typ hinzufügen.

Um inkompatible Änderungen zu vermeiden, werden Makros, die als l-Wert in einer Zuweisung verwendet werden können, nicht umgewandelt.

Begründung

Die Verwendung von Makros kann unbeabsichtigte negative Auswirkungen haben, die selbst für erfahrene C-Entwickler schwer zu vermeiden sind. Einige Probleme sind seit Jahren bekannt, andere wurden kürzlich in Python entdeckt. Die Umgehung von Makro-Fallstricken macht den Makro-Code schwerer lesbar und wartbar.

Die Umwandlung von Makros in Funktionen hat mehrere Vorteile

  • Funktionen leiden nicht unter Makro-Fallstricken, zum Beispiel den folgenden, die in der GCC-Dokumentation beschrieben sind
    • Falsche Verschachtelung
    • Probleme mit der Operator-Priorität
    • Das Semikolon „schlucken“
    • Duplizierung von Nebeneffekten
    • Sich selbst referenzierende Makros
    • Argument-Vorschau
    • Zeilenumbrüche in Argumenten

    Funktionen benötigen nicht die folgenden Workarounds für Makro-Fallstricke, wodurch sie in der Regel leichter zu lesen und zu warten sind als vergleichbarer Makro-Code

    • Hinzufügen von Klammern um Argumente.
    • Verwendung von Zeilenfortsetzungszeichen, wenn die Funktion über mehrere Zeilen geschrieben wird.
    • Hinzufügen von Kommas zur Ausführung mehrerer Ausdrücke.
    • Verwendung von do { ... } while (0), um mehrere Anweisungen zu schreiben.
  • Argumenttypen und der Rückgabetyp von Funktionen sind klar definiert.
  • Debugger und Profiler können den Namen von inlined Funktionen abrufen.
  • Debugger können Breakpoints in inlined Funktionen setzen.
  • Variablen haben einen klar definierten Geltungsbereich.

Die Umwandlung von Makros und statischen Inline-Funktionen in reguläre Funktionen macht diese regulären Funktionen für Projekte zugänglich, die Python verwenden, aber keine Makros und statischen Inline-Funktionen nutzen können.

Spezifikation

Statische Inline-Funktionen als Ersatz für Makros

Die meisten Makros werden in statische Inline-Funktionen umgewandelt.

Die folgenden Makros werden nicht umgewandelt

  • Objektähnliche Makros (d.h. solche, die keine Klammern und Argumente benötigen). Zum Beispiel
    • Leere Makros. Beispiel: #define Py_HAVE_CONDVAR.
    • Nur einen Wert definierende Makros, auch wenn eine Konstante mit einem gut definierten Typ besser wäre. Beispiel: #define METH_VARARGS 0x0001.
  • Kompatibilitätsschicht für verschiedene C-Compiler, C-Spracherweiterungen oder neuere C-Funktionen. Beispiel: Py_GCC_ATTRIBUTE(), Py_ALWAYS_INLINE, Py_MEMCPY().
  • Makros, die für Definitionen und nicht für Verhalten verwendet werden. Beispiel: PyAPI_FUNC, Py_DEPRECATED, Py_PYTHON_H.
  • Makros, die C-Präprozessor-Funktionen wie Stringifizierung und Konkatenation benötigen. Beispiel: Py_STRINGIFY().
  • Makros, die nicht in Funktionen umgewandelt werden können. Beispiele: Py_BEGIN_ALLOW_THREADS (enthält ein nicht zugeordnetes }), Py_VISIT (verlässt sich auf spezifische Variablennamen), Py_RETURN_RICHCOMPARE (gibt aus der aufrufenden Funktion zurück).
  • Makros, die als l-Wert in Zuweisungen verwendet werden können. Dies wäre eine inkompatible Änderung und liegt außerhalb des Rahmens dieses PEP. Beispiel: PyBytes_AS_STRING().
  • Makros mit unterschiedlichen Rückgabetypen, abhängig vom Code-Pfad oder den Argumenten.

Reguläre Funktionen als Ersatz für statische Inline-Funktionen

Statische Inline-Funktionen in der öffentlichen C API können in reguläre Funktionen umgewandelt werden, jedoch nur, wenn keine messbaren Leistungseinbußen durch die Änderung der Funktion entstehen. Die Leistungseinbußen sollten anhand von Benchmarks gemessen werden.

Zeigerargumente casten

Derzeit casten die meisten Makros, die Zeiger akzeptieren, Zeigerargumente auf ihre erwarteten Typen. Zum Beispiel, in Python 3.6, castet das Py_TYPE() Makro sein Argument zu PyObject*

#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)

Das Py_TYPE() Makro akzeptiert den Typ PyObject*, aber auch beliebige Zeigertypen, wie PyLongObject* und PyDictObject*.

Funktionen sind stark typisiert und können nur einen Argumenttyp akzeptieren.

Um Compiler-Fehler und -Warnungen im bestehenden Code zu vermeiden, wird, wenn ein Makro in eine Funktion umgewandelt wird und das Makro mindestens eines seiner Argumente castet, ein neues Makro hinzugefügt, um den Cast beizubehalten. Das neue Makro und die Funktion erhalten denselben Namen.

Beispiel für das in eine statische Inline-Funktion umgewandelte Makro Py_TYPE()

static inline PyTypeObject* Py_TYPE(PyObject *ob) {
    return ob->ob_type;
}
#define Py_TYPE(ob) Py_TYPE((PyObject*)(ob))

Der Cast wird für alle Zeigertypen beibehalten, nicht nur für PyObject*. Dies schließt Casts zu void* ein: das Entfernen eines Casts zu void* würde eine neue Warnung ausgeben, wenn die Funktion mit einer const void*-Variable aufgerufen wird. Zum Beispiel castet das Makro PyUnicode_WRITE() sein data-Argument zu void*, und akzeptiert daher derzeit den Typ const void*, obwohl es in data schreibt. Dieser PEP wird dies nicht ändern.

Cast in der Limited C API Version 3.11 vermeiden

Die Casts werden aus der Limited C API Version 3.11 und neueren Versionen ausgeschlossen. Wenn ein API-Benutzer die neue Limited API aktiviert, muss er die erwarteten Typen übergeben oder den Cast durchführen.

Als Beispiel wird Py_TYPE() wie folgt definiert

static inline PyTypeObject* Py_TYPE(PyObject *ob) {
    return ob->ob_type;
}
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
#  define Py_TYPE(ob) Py_TYPE((PyObject*)(ob))
#endif

Rückgabetyp unverändert

Wenn ein Makro in eine Funktion umgewandelt wird, darf sein Rückgabetyp nicht geändert werden, um die Ausgabe neuer Compiler-Warnungen zu verhindern.

Zum Beispiel änderte Python 3.7 den Rückgabetyp von PyUnicode_AsUTF8() von char* zu const char* (Commit). Die Änderung gab neue Compiler-Warnungen aus, wenn C-Erweiterungen kompiliert wurden, die char* erwarteten. Dieser PEP ändert den Rückgabetyp nicht, um dieses Problem zu vermeiden.

Abwärtskompatibilität

Der PEP wurde entwickelt, um inkompatible Änderungen der C API zu vermeiden.

Nur C-Erweiterungen, die explizit auf die Limited C API Version 3.11 abzielen, müssen jetzt die erwarteten Typen an Funktionen übergeben: Zeigerargumente werden nicht mehr in die erwarteten Typen gecastet.

Funktionsargumente vom Zeigertyp werden weiterhin gecastet und Rückgabetypen werden nicht geändert, um die Ausgabe neuer Compiler-Warnungen zu verhindern.

Makros, die als l-Wert in einer Zuweisung verwendet werden können, werden von diesem PEP nicht modifiziert, um inkompatible Änderungen zu vermeiden.

Beispiele für Makro-Fallstricke

Duplizierung von Nebeneffekten

Makros

#define PySet_Check(ob) \
    (Py_IS_TYPE(ob, &PySet_Type) \
     || PyType_IsSubtype(Py_TYPE(ob), &PySet_Type))

#define Py_IS_NAN(X) ((X) != (X))

Wenn das Argument op oder X einen Nebeneffekt hat, wird der Nebeneffekt dupliziert: Er wird zweimal von PySet_Check() und Py_IS_NAN() ausgeführt.

Zum Beispiel hat das Argument pos++ im Code PyUnicode_WRITE(kind, data, pos++, ch) einen Nebeneffekt. Dieser Code ist sicher, da das PyUnicode_WRITE() Makro sein 3. Argument nur einmal verwendet und somit den pos++ Nebeneffekt nicht dupliziert.

Falsche Verschachtelung

Beispiel für den bpo-43181: Python Makros schirmen Argumente nicht ab. Das PyObject_TypeCheck() Makro vor seiner Korrektur

#define PyObject_TypeCheck(ob, tp) \
    (Py_IS_TYPE(ob, tp) || PyType_IsSubtype(Py_TYPE(ob), (tp)))

C++-Nutzungsbeispiel

PyObject_TypeCheck(ob, U(f<a,b>(c)))

Der Präprozessor expandiert es zuerst

(Py_IS_TYPE(ob, f<a,b>(c)) || ...)

C++-Zeichen "<" und ">" werden vom Präprozessor nicht als Klammern behandelt, sodass das Makro Py_IS_TYPE() mit 3 Argumenten aufgerufen wird

  • ob
  • f<a
  • b>(c)

Die Kompilierung schlägt mit einem Fehler bei Py_IS_TYPE() fehl, das nur 2 Argumente akzeptiert.

Der Fehler liegt darin, dass die Argumente op und tp von PyObject_TypeCheck() in Klammern gesetzt werden müssen: Ersetzen Sie Py_IS_TYPE(ob, tp) durch Py_IS_TYPE((ob), (tp)). Im regulären C-Code sind diese Klammern redundant, können als Fehler angesehen werden und werden daher beim Schreiben von Makros oft vergessen.

Um Makro-Fallstricke zu vermeiden, wurde das Makro PyObject_TypeCheck() in eine statische Inline-Funktion umgewandelt: Commit.

Beispiele für schwer lesbare Makros

PyObject_INIT()

Beispiel, das die Verwendung von Kommas in einem Makro zeigt, das einen Rückgabewert hat.

Python 3.7 Makro

#define PyObject_INIT(op, typeobj) \
    ( Py_TYPE(op) = (typeobj), _Py_NewReference((PyObject *)(op)), (op) )

Python 3.8 Funktion (vereinfachter Code)

static inline PyObject*
_PyObject_INIT(PyObject *op, PyTypeObject *typeobj)
{
    Py_TYPE(op) = typeobj;
    _Py_NewReference(op);
    return op;
}

#define PyObject_INIT(op, typeobj) \
    _PyObject_INIT(_PyObject_CAST(op), (typeobj))
  • Die Funktion benötigt nicht das Zeilenfortsetzungszeichen "\".
  • Sie hat ein explizites "return op;" anstelle der überraschenden ", (op)" Syntax am Ende des Makros.
  • Sie verwendet kurze Anweisungen auf mehreren Zeilen, anstatt als eine einzige lange Zeile geschrieben zu werden.
  • Innerhalb der Funktion hat das Argument op den wohldefinierten Typ PyObject* und benötigt daher keine Casts wie (PyObject *)(op).
  • Argumente müssen nicht in Klammern gesetzt werden: Verwenden Sie typeobj anstelle von (typeobj).

_Py_NewReference()

Beispiel, das die Verwendung einer #ifdef innerhalb eines Makros zeigt.

Python 3.7 Makro (vereinfachter Code)

#ifdef COUNT_ALLOCS
#  define _Py_INC_TPALLOCS(OP) inc_count(Py_TYPE(OP))
#  define _Py_COUNT_ALLOCS_COMMA  ,
#else
#  define _Py_INC_TPALLOCS(OP)
#  define _Py_COUNT_ALLOCS_COMMA
#endif /* COUNT_ALLOCS */

#define _Py_NewReference(op) (                   \
    _Py_INC_TPALLOCS(op) _Py_COUNT_ALLOCS_COMMA  \
    Py_REFCNT(op) = 1)

Python 3.8 Funktion (vereinfachter Code)

static inline void _Py_NewReference(PyObject *op)
{
    _Py_INC_TPALLOCS(op);
    Py_REFCNT(op) = 1;
}

PyUnicode_READ_CHAR()

Dieses Makro verwendet Argumente wieder und ruft möglicherweise PyUnicode_KIND mehrmals auf

#define PyUnicode_READ_CHAR(unicode, index) \
(assert(PyUnicode_Check(unicode)),          \
 assert(PyUnicode_IS_READY(unicode)),       \
 (Py_UCS4)                                  \
    (PyUnicode_KIND((unicode)) == PyUnicode_1BYTE_KIND ? \
        ((const Py_UCS1 *)(PyUnicode_DATA((unicode))))[(index)] : \
        (PyUnicode_KIND((unicode)) == PyUnicode_2BYTE_KIND ? \
            ((const Py_UCS2 *)(PyUnicode_DATA((unicode))))[(index)] : \
            ((const Py_UCS4 *)(PyUnicode_DATA((unicode))))[(index)] \
        ) \
    ))

Mögliche Implementierung als statisch inlined Funktion

static inline Py_UCS4
PyUnicode_READ_CHAR(PyObject *unicode, Py_ssize_t index)
{
    assert(PyUnicode_Check(unicode));
    assert(PyUnicode_IS_READY(unicode));

    switch (PyUnicode_KIND(unicode)) {
    case PyUnicode_1BYTE_KIND:
        return (Py_UCS4)((const Py_UCS1 *)(PyUnicode_DATA(unicode)))[index];
    case PyUnicode_2BYTE_KIND:
        return (Py_UCS4)((const Py_UCS2 *)(PyUnicode_DATA(unicode)))[index];
    case PyUnicode_4BYTE_KIND:
    default:
        return (Py_UCS4)((const Py_UCS4 *)(PyUnicode_DATA(unicode)))[index];
    }
}

Seit Python 3.8 in Funktionen umgewandelte Makros

Dies ist eine Liste von Makros, die bereits zwischen Python 3.8 und Python 3.11 in Funktionen umgewandelt wurden. Obwohl einige umgewandelte Makros (wie Py_INCREF()) sehr häufig von C-Erweiterungen verwendet werden, hatten diese Umwandlungen keinen signifikanten Einfluss auf die Python-Leistung und die meisten brachen die Rückwärtskompatibilität nicht.

Makros in statische Inline-Funktionen umgewandelt

Python 3.8

  • Py_DECREF()
  • Py_INCREF()
  • Py_XDECREF()
  • Py_XINCREF()
  • PyObject_INIT()
  • PyObject_INIT_VAR()
  • _PyObject_GC_UNTRACK()
  • _Py_Dealloc()

Makros in reguläre Funktionen umgewandelt

Python 3.9

  • PyIndex_Check()
  • PyObject_CheckBuffer()
  • PyObject_GET_WEAKREFS_LISTPTR()
  • PyObject_IS_GC()
  • PyObject_NEW(): Alias für PyObject_New()
  • PyObject_NEW_VAR(): Alias für PyObjectVar_New()

Um Leistungseinbußen bei ohne LTO gebauten Python-Versionen zu vermeiden, wurden private statische Inline-Funktionen zur internen C-API hinzugefügt

  • _PyIndex_Check()
  • _PyObject_IS_GC()
  • _PyType_HasFeature()
  • _PyType_IS_GC()

Statische Inline-Funktionen in reguläre Funktionen umgewandelt

Python 3.11

  • PyObject_CallOneArg()
  • PyObject_Vectorcall()
  • PyVectorcall_Function()
  • _PyObject_FastCall()

Um Leistungseinbußen bei ohne LTO gebauten Python-Versionen zu vermeiden, wurde eine private statische Inline-Funktion zur internen C-API hinzugefügt

  • _PyVectorcall_FunctionInline()

Inkompatible Änderungen

Während andere umgewandelte Makros die Abwärtskompatibilität nicht brachen, gibt es eine Ausnahme.

Die 3 Makros Py_REFCNT(), Py_TYPE() und Py_SIZE() wurden in Python 3.10 und 3.11 in statische Inline-Funktionen umgewandelt, um die Verwendung als l-Wert in Zuweisungen zu unterbinden. Dies ist eine absichtlich vorgenommene inkompatible Änderung: siehe bpo-39573 für die Begründung.

Dieser PEP schlägt keine Umwandlung von Makros vor, die als l-Wert verwendet werden können, um neue inkompatible Änderungen zu vermeiden.

Leistungsbedenken und Benchmarks

Es gab Bedenken, dass die Umwandlung von Makros in Funktionen die Leistung beeinträchtigen kann.

Dieser Abschnitt erklärt Leistungsprobleme und zeigt Benchmark-Ergebnisse unter Verwendung von PR 29728, der die folgenden statischen Inline-Funktionen durch Makros ersetzt

  • PyObject_TypeCheck()
  • PyType_Check(), PyType_CheckExact()
  • PyType_HasFeature()
  • PyVectorcall_NARGS()
  • Py_DECREF(), Py_XDECREF()
  • Py_INCREF(), Py_XINCREF()
  • Py_IS_TYPE()
  • Py_NewRef()
  • Py_REFCNT(), Py_TYPE(), Py_SIZE()

Die Benchmarks wurden auf Fedora 35 (Linux) mit GCC 11 auf einem Laptop mit 8 logischen CPUs (4 physische CPU-Kerne) ausgeführt.

Statische Inline-Funktionen

Erstens hat die Umwandlung von Makros in *statische Inline*-Funktionen kaum Einfluss auf die Leistung: die gemessenen Unterschiede sind konsistent mit Rauschen aufgrund nicht verwandter Faktoren.

Statische Inline-Funktionen sind eine neue Funktion im C99-Standard. Moderne C-Compiler verfügen über effiziente Heuristiken, um zu entscheiden, ob eine Funktion inlined werden soll oder nicht.

Wenn ein C-Compiler entscheidet, nicht zu inlinen, gibt es wahrscheinlich einen guten Grund. Zum Beispiel würde das Inlinen ein Register wiederverwenden, was das Speichern/Wiederherstellen des Registerwerts auf dem Stack erfordert und somit den Stack-Speicherverbrauch erhöht oder weniger effizient ist.

Benchmark des Befehls ./python -m test -j5 auf Python, kompiliert im Release-Modus mit gcc -O3, LTO und PGO

  • Makros (PR 29728): 361 Sek. ± 1 Sek.
  • Statische Inline-Funktionen (Referenz): 361 Sek. ± 1 Sek.

Es gibt **keinen signifikanten Leistungsunterschied** zwischen Makros und statischen Inline-Funktionen, wenn statische Inline-Funktionen **inlined werden**.

Debug-Build

Die Leistung in Debug-Builds *kann* leiden, wenn Makros in Funktionen umgewandelt werden. Dies wird durch verbesserte Debugging-Fähigkeiten kompensiert: Debugger können Funktionsnamen abrufen, Breakpoints in Funktionen setzen usw.

Unter Windows werden statische Inline-Funktionen, wenn Python im Debug-Modus mit Visual Studio kompiliert wird, nicht inlined.

Auf anderen Plattformen verwendet ./configure --with-pydebug die Compiler-Option -Og auf Compilern, die dies unterstützen (einschließlich GCC und LLVM Clang). -Og bedeutet „optimiertes Debugging-Erlebnis“. Andernfalls wird die Compiler-Option -O0 verwendet. -O0 bedeutet „deaktiviere die meisten Optimierungen“.

Mit GCC 11 kann gcc -Og statische Inline-Funktionen inlinen, während gcc -O0 statische Inline-Funktionen nicht inlinen kann.

Benchmark des Befehls ./python -m test -j10 auf Python, kompiliert im Debug-Modus mit gcc -O0 (d.h. Compiler-Optimierungen, einschließlich Inlining, sind explizit deaktiviert)

  • Makros (PR 29728): 345 Sek. ± 5 Sek.
  • Statische Inline-Funktionen (Referenz): 360 Sek. ± 6 Sek.

Das Ersetzen von Makros durch statische Inline-Funktionen macht Python **1,04x langsamer**, wenn der Compiler statische Inline-Funktionen **nicht inlined**.

Beachten Sie, dass Benchmarks nicht auf einem Python-Debug-Build ausgeführt werden sollten. Darüber hinaus wird die Verwendung von Link-Time Optimization (LTO) und Profile-Guided Optimization (PGO) für optimale Leistung und zuverlässige Benchmarks empfohlen. PGO hilft dem Compiler zu entscheiden, ob Funktionen inlined werden sollen oder nicht.

Erzwingen des Inlinings

Das Makro Py_ALWAYS_INLINE kann verwendet werden, um das Inlinen zu erzwingen. Dieses Makro verwendet __attribute__((always_inline)) mit GCC und Clang und __forceinline mit MSC.

Frühere Versuche, Py_ALWAYS_INLINE zu verwenden, zeigten keinen Nutzen und wurden aufgegeben. Siehe zum Beispiel bpo-45094 „Consider using __forceinline and __attribute__((always_inline)) on static inline functions (Py_INCREF, Py_TYPE) for debug build“.

Als das Makro Py_INCREF() im Jahr 2018 in eine statische Inline-Funktion umgewandelt wurde (Commit), wurde beschlossen, das Inlining nicht zu erzwingen. Der Maschinencode wurde mit mehreren C-Compilern und Compiler-Optionen analysiert, und Py_INCREF() wurde immer ohne erzwingendes Inlining inlined. Der einzige Fall, in dem es nicht inlined wurde, war der Debug-Build. Siehe Diskussion in bpo-35059 „Convert Py_INCREF() and PyObject_INIT() to inlined functions“.

Deaktivieren des Inlinings

Auf der anderen Seite kann das Makro Py_NO_INLINE verwendet werden, um das Inlinen zu deaktivieren. Es kann verwendet werden, um den Stack-Speicherverbrauch zu reduzieren oder das Inlinen bei LTO+PGO-Builds zu verhindern, die generell Code aggressiver inlinen: siehe bpo-33720. Das Makro Py_NO_INLINE verwendet __attribute__ ((noinline)) mit GCC und Clang und __declspec(noinline) mit MSC.

Diese Technik ist verfügbar, obwohl wir derzeit keine konkrete Funktion kennen, für die sie nützlich wäre. Beachten Sie, dass es bei Makros nicht möglich ist, das Inlinen überhaupt zu deaktivieren.

Abgelehnte Ideen

Makros beibehalten, aber einige Probleme beheben

Makros werden immer mit jedem C-Compiler „inlined“.

Die Duplizierung von Nebeneffekten kann vom Aufrufer des Makros umgangen werden.

Benutzer von Makros sollten als „einwilligende Erwachsene“ betrachtet werden. Leute, die sich mit Makros unsicher fühlen, sollten sie einfach nicht verwenden.

Diese Ideen werden verworfen, da Makros fehleranfällig *sind* und es zu leicht ist, einen Makro-Fallstrick beim Schreiben und Überprüfen von Makro-Code zu übersehen. Darüber hinaus sind Makros schwerer zu lesen und zu warten als Funktionen.

Post-Historie

Threads der python-dev Mailingliste

Referenzen

  • bpo-45490: [C API] PEP 670: Makros in der Python C API in Funktionen umwandeln (Oktober 2021).
  • Was tun mit unsicheren Makros (März 2021).
  • bpo-43502: [C-API] Offensichtlich unsichere Makros in statische Inline-Funktionen umwandeln (März 2021).

Versionshistorie

  • Version 2
    • Strengere Richtlinie bezüglich der Nichtänderung von Argumenttypen und Rückgabetyp.
    • Bessere Erklärung, warum Zeigerargumente einen Cast benötigen, um keine neuen Compiler-Warnungen auszugeben.
    • Makros, die als l-Werte verwendet werden können, werden vom PEP nicht mehr modifiziert.
    • Makros mit mehreren Rückgabetypen werden vom PEP nicht mehr modifiziert.
    • Die Limited C API Version 3.11 castet Zeigerargumente nicht mehr.
    • Rückgabewerte von Makros, „die keinen Rückgabewert haben sollten“, werden nicht mehr entfernt.
    • Hinzufügen des Abschnitts „Seit Python 3.8 in Funktionen umgewandelte Makros“.
    • Hinzufügen des Abschnitts „Benchmark zum Vergleich von Makros und statischen Inline-Funktionen“.
  • Version 1: Erste öffentliche Version

Quelle: https://github.com/python/peps/blob/main/peps/pep-0670.rst

Zuletzt geändert: 2025-02-01 08:55:40 GMT