PEP 576 – Rationalisierung von eingebauten Funktionsklassen
- Autor:
- Mark Shannon <mark at hotpy.org>
- BDFL-Delegate:
- Petr Viktorin
- Status:
- Zurückgezogen
- Typ:
- Standards Track
- Erstellt:
- 10. Mai 2018
- Python-Version:
- 3.8
- Post-History:
- 17. Mai 2018, 23. Juni 2018, 08. Juli 2018, 29. März 2019
Zusammenfassung
Die intern von CPython verwendete „FastcallKeywords“-Konvention für Callable-Objekte von Drittanbietern freigeben und das Modul inspect zur Duck-Typisierung verwenden. In Kombination wird dies Drittanbieter-C-Erweiterungen und Tools wie Cython ermöglichen, Objekte zu erstellen, die die gleichen Aufrufkonventionen wie eingebaute und Python-Funktionen verwenden und somit Leistungsparität mit eingebauten Funktionen wie len oder print erzielen.
Eine geringfügige Leistungsverbesserung bestehenden Codes wird erwartet.
Motivation
Derzeit stehen Autoren von Modulen von Drittanbietern vor einem Dilemma bei der Implementierung von Funktionen in C. Entweder sie können eine der bereits vorhandenen eingebauten Funktions- oder Methodenklassen verwenden oder sie implementieren ihre eigene benutzerdefinierte Klasse in C. Die erste Wahl führt zum Verlust der Fähigkeit, auf die Interna des Callable-Objekts zuzugreifen. Die zweite Wahl stellt eine zusätzliche Wartungsbelastung dar und, was noch wichtiger ist, hat erhebliche negative Auswirkungen auf die Leistung.
Dieser PEP zielt darauf ab, Autoren von C-Modulen von Drittanbietern und Tools wie Cython die schnellere Aufrufkonvention zu ermöglichen, die von CPython intern für eingebaute Funktionen und Methoden verwendet wird, und dies ohne Verlust von Funktionen im Vergleich zu einer in Python implementierten Funktion.
Introspektion
Das Modul inspect unterstützt die Duck-Typisierung bei der Introspektion von Callable-Objekten vollständig.
Die Funktion inspect.Signature.from_callable() berechnet die Signatur eines Callable-Objekts. Wenn ein Objekt eine __signature__-Eigenschaft hat, gibt inspect.Signature.from_callable() diese einfach zurück. Zur weiteren Unterstützung der Duck-Typisierung, wenn ein Callable-Objekt eine __text_signature__ hat, wird die __signature__ daraus erstellt.
Das bedeutet, dass 3rd-Party-Builtin-Funktionen __text_signature__ implementieren können, wenn dies ausreichend ist, und die aufwändigere __signature__, wenn nötig.
Effiziente Aufrufe von Callable-Objekten von Drittanbietern
Derzeit werden die meisten Aufrufe an function und method_descriptor in benutzerdefiniertem Code unter Verwendung der internen „FastcallKeywords“-Aufrufkonvention weitergeleitet. Dieser PEP schlägt vor, dass diese Aufrufkonvention über einen C-Funktionszeiger implementiert wird. Drittanbieter-Callable-Objekte, die diese Binärschnittstelle implementieren, haben das Potenzial, so schnell aufgerufen zu werden wie eine eingebaute Funktion.
Fortgesetztes Verbot von Callable-Klassen als Basisklassen
Derzeit schlägt jeder Versuch, function, method oder method_descriptor als Basisklasse für eine neue Klasse zu verwenden, mit einem TypeError fehl. Dieses Verhalten ist wünschenswert, da es Fehler vermeidet, wenn eine Unterklasse die __call__-Methode überschreibt. Wenn Callable-Objekte Unterklassen bilden könnten, müsste jeder Aufruf einer function oder eines method_descriptor eine zusätzliche Prüfung beinhalten, ob die __call__-Methode nicht überschrieben wurde. Durch die Bereitstellung eines zusätzlichen Aufrufmechanismus wird das Fehlerrisiko größer. Infolgedessen kann jede Drittanbieter-Klasse, die die zusätzliche Aufrufschnittstelle implementiert, nicht als Basisklasse verwendet werden.
Neue Klassen und Änderungen an bestehenden Klassen
Für Python sichtbare Änderungen
- Eine neue eingebaute Klasse,
builtin_function, wird hinzugefügt. types.BuiltinFunctionTypewird aufbuiltin_functionverweisen und nicht aufbuiltin_function_or_method.- Instanzen der Klasse
builtin_functionbehalten die__module__-Eigenschaft vonbuiltin_function_or_methodund erhalten die Eigenschaftenfunc_moduleundfunc_globals.func_moduleermöglicht den Zugriff auf das Modul, zu dem die Funktion gehört. Beachten Sie, dass dies anders ist als die Eigenschaft__module__, die lediglich den Namen des Moduls zurückgibt. Die Eigenschaftfunc_globalsist äquivalent zufunc_module.__dict__und wird bereitgestellt, um die gleichnamige Eigenschaft der Python-Funktion zu emulieren. - Beim Binden einer Instanz von
method_descriptoran eine Instanz ihrer besitzenden Klasse wird anstelle vonbuiltin_function_or_methodeinebound_methoderstellt. Das bedeutet, dass diemethod_descriptorsnun das Verhalten von Python-Funktionen genauer emulieren. Mit anderen Worten,[].appendwird zu einerbound_methodund nicht zu einerbuiltin_function_or_method.
Änderungen an der C-API
- Eine neue Funktion
PyBuiltinFunction_New(PyMethodDef *ml, PyObject *module)wird hinzugefügt, um eingebaute Funktionen zu erstellen. PyCFunction_NewEx()undPyCFunction_New()werden als veraltet markiert und geben, wenn möglich, einePyBuiltinFunctionzurück, andernfalls einebuiltin_function_or_method.
Aufrechterhaltung der Abwärtskompatibilität bei der C-API und ABI
Die vorgeschlagenen Änderungen sind auf API- und ABI-Ebene vollständig abwärts- und vorwärtskompatibel.
Interne C-Änderungen
Zwei neue Flags dürfen für das Feld typeobject.tp_flags verwendet werden. Dies sind Py_TPFLAGS_EXTENDED_CALL und Py_TPFLAGS_FUNCTION_DESCRIPTOR
Py_TPFLAGS_EXTENDED_CALL
Für jede eingebaute Klasse, die Py_TPFLAGS_EXTENDED_CALL setzt, muss die C-Struktur, die dieser eingebauten Klasse entspricht, mit der Struktur PyExtendedCallable beginnen, die wie folgt definiert ist:
typedef PyObject *(*extended_call_ptr)(PyObject *callable, PyObject** args,
int positional_argcount, PyTupleObject* kwnames);
typedef struct {
PyObject_HEAD
extended_call_ptr ext_call;
} PyExtendedCallable;
Jede Klasse, die Py_TPFLAGS_EXTENDED_CALL setzt, kann nicht als Basisklasse verwendet werden, und ein TypeError wird ausgelöst, wenn Python-Code versucht, sie als Basisklasse zu verwenden.
Py_TPFLAGS_FUNCTION_DESCRIPTOR
Wenn dieses Flag für eine eingebaute Klasse F gesetzt ist, wird erwartet, dass Instanzen dieser Klasse sich wie eine Python-Funktion verhalten, wenn sie als Klassenattribut verwendet werden. Insbesondere bedeutet dies, dass der Wert von c.m, wobei C.m eine Instanz der eingebauten Klasse F ist (und c eine Instanz von C ist), eine gebundene Methode sein muss, die C.m und c bindet. Ohne dieses Flag wäre es für benutzerdefinierte Callable-Objekte unmöglich, wie Python-Funktionen zu funktionieren *und* dabei so effizient wie Python- oder eingebaute Funktionen zu sein.
Änderungen an bestehenden C-Strukturen
Die Klassen function, method_descriptor und method werden ihre entsprechenden Strukturen so geändert, dass sie mit der Struktur PyExtendedCallable beginnen.
Drittanbieter-eingebaute Klassen, die die neue erweiterte Aufrufschnittstelle verwenden
Um eine Aufrufsleistung auf dem Niveau von Python-Funktionen und eingebauten Funktionen zu ermöglichen, sollten Drittanbieter-Callable-Objekte das Bit Py_TPFLAGS_EXTENDED_CALL von tp_flags setzen und sicherstellen, dass die entsprechende C-Struktur mit PyExtendedCallable beginnt. Jede eingebaute Klasse, bei der das Bit Py_TPFLAGS_EXTENDED_CALL gesetzt ist, muss auch die Funktion tp_call implementieren und sicherstellen, dass ihr Verhalten mit der Funktion ext_call konsistent ist.
Leistungsimplikationen dieser Änderungen
Das Hinzufügen eines Funktionszeigers zu jedem Callable-Objekt anstelle jeder Klasse von Callable-Objekten ermöglicht die Wahl der Dispatching-Funktion (des Codes zum Mischen von Argumenten und zur Fehlerprüfung) zum Zeitpunkt der Erstellung des Callable-Objekts anstatt zum Zeitpunkt des Aufrufs. Dies sollte die Anzahl der zwischen dem Aufruf-Site im Interpreter und der Ausführung des Aufgerufenen ausgeführten Instruktionen reduzieren.
Alternative Vorschläge
PEP 580 ist ein alternativer Ansatz zur Lösung desselben Problems wie dieser PEP.
Referenzimplementierung
Eine vorläufige Implementierung finden Sie unter https://github.com/markshannon/cpython/tree/pep-576-minimal
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0576.rst
Zuletzt geändert: 2025-02-01 08:55:40 GMT