PEP 3113 – Entfernung von Tupel-Parameter-Entpackung
- Autor:
- Brett Cannon <brett at python.org>
- Status:
- Final
- Typ:
- Standards Track
- Erstellt:
- 02.03.2007
- Python-Version:
- 3.0
- Post-History:
Zusammenfassung
Tupel-Parameter-Entpackung ist die Verwendung eines Tupels als Parameter in einer Funktionssignatur, um ein Sequenzargument automatisch entpacken zu lassen. Ein Beispiel ist
def fxn(a, (b, c), d):
pass
Die Verwendung von (b, c) in der Signatur erfordert, dass das zweite Argument der Funktion eine Sequenz der Länge zwei ist (z. B. [42, -13]). Wenn eine solche Sequenz übergeben wird, wird sie entpackt und ihre Werte werden den Parametern zugewiesen, so als ob die Anweisung b, c = [42, -13] in den Parametern ausgeführt worden wäre.
Leider verursacht dieses Merkmal von Pythons reichhaltigen Funktionssignatur-Fähigkeiten, obwohl es in einigen Situationen praktisch ist, mehr Probleme als sie wert sind. Daher schlägt dieses PEP ihre Entfernung aus der Sprache in Python 3.0 vor.
Warum sie gehen sollten
Introspektionsprobleme
Python hat sehr leistungsfähige Introspektionsfähigkeiten. Diese erstrecken sich auf Funktionssignaturen. Es gibt keine versteckten Details darüber, wie die Aufrufsignatur einer Funktion aussieht. Im Allgemeinen ist es recht einfach, verschiedene Details über die Signatur einer Funktion herauszufinden, indem man das Funktionsobjekt und verschiedene Attribute davon betrachtet (einschließlich des func_code-Attributs der Funktion).
Aber es gibt große Schwierigkeiten, wenn es um Tupelparameter geht. Die Existenz eines Tupelparameters wird durch seinen Namen gekennzeichnet, der aus einem . und einer Zahl im co_varnames-Attribut des Codeobjekts der Funktion besteht. Dies ermöglicht es, das Tupelargument einem Namen zuzuweisen, der nur dem Bytecode bekannt ist und nicht im Python-Quellcode eingegeben werden kann. Dies spezifiziert jedoch nicht das Format des Tupels: seine Länge, ob verschachtelte Tupel vorhanden sind, etc.
Um alle Details über das Tupel von der Funktion zu erhalten, muss man den Bytecode der Funktion analysieren. Dies liegt daran, dass der erste Bytecode in der Funktion wörtlich in die Entpackung des Tupelarguments übersetzt wird. Angenommen, der Tupelparameter heißt .1 und soll in die Variablen spam und monty entpackt werden (was bedeutet, dass es sich um das Tupel (spam, monty) handelt), dann wird der erste Bytecode in der Funktion für die Anweisung spam, monty = .1 sein. Das bedeutet, um alle Details des Tupelparameters zu kennen, muss man den anfänglichen Bytecode der Funktion betrachten, um Tupel-Entpackungen für Parameter im Format \.\d+ zu erkennen und alle Informationen über das erwartete Argument abzuleiten. Bytecode-Analyse ist, wie die Funktion inspect.getargspec Informationen über Tupelparameter bereitstellen kann. Dies ist nicht einfach zu tun und ist für Introspektionswerkzeuge eine Belastung, da sie wissen müssen, wie Pythons Bytecode funktioniert (eine ansonsten unnötige Belastung, da alle anderen Arten von Parametern kein Wissen über Pythons Bytecode erfordern).
Abgesehen von der Schwierigkeit der Bytecode-Analyse gibt es ein weiteres Problem mit der Abhängigkeit von der Verwendung von Python-Bytecode. IronPython [3] verwendet nicht Pythons Bytecode. Da es auf dem .NET-Framework basiert, speichert es stattdessen MSIL [4] im func_code.co_code-Attribut der Funktion. Diese Tatsache verhindert, dass die Funktion inspect.getargspec unter IronPython funktioniert. Es ist unbekannt, ob andere Python-Implementierungen betroffen sind, aber es ist vernünftig anzunehmen, wenn die Implementierung nicht nur eine Neuimplementierung der Python-Virtuellen-Maschine ist.
Kein Verlust von Fähigkeiten bei Entfernung
Wie in Introspektionsprobleme erwähnt, beginnt der Bytecode der Funktion für die Handhabung von Tupelparametern mit dem Bytecode, der zum Entpacken des Arguments in die entsprechenden Parameternamen erforderlich ist. Das bedeutet, dass keine spezielle Unterstützung für die Implementierung von Tupelparametern erforderlich ist und somit kein Verlust von Fähigkeiten entsteht, wenn sie entfernt würden, nur ein möglicher Komfort (der in Warum sie (angeblich) bleiben sollten behandelt wird).
Die Beispiel-Funktion am Anfang dieses PEP könnte leicht umgeschrieben werden als
def fxn(a, b_c, d):
b, c = b_c
pass
und in keiner Weise an Funktionalität verlieren.
Ausnahme von der Regel
Wenn man sich die verschiedenen Arten von Parametern ansieht, die eine Python-Funktion haben kann, wird man feststellen, dass Tupelparameter eher die Ausnahme als die Regel sind.
Betrachten Sie PEP 3102 (Nur-Schlüsselwort-Argumente) und PEP 3107 (Funktionsannotationen). Beide PEPs wurden akzeptiert und führen neue Funktionalität innerhalb der Signatur einer Funktion ein. Und doch kann für beide PEPs das neue Merkmal nicht auf Tupelparameter als Ganzes angewendet werden. PEP 3102 hat keinerlei Unterstützung für Tupelparameter (was sinnvoll ist, da es keine Möglichkeit gibt, einen Tupelparameter namentlich zu referenzieren). PEP 3107 erlaubt Annotationen für jedes Element innerhalb des Tupels (z. B. (x:int, y:int)), aber nicht für das gesamte Tupel (z. B. (x, y):int).
Die Existenz von Tupelparametern trennt auch Sequenzobjekte von Mapping-Objekten in einer Funktionssignatur. Es gibt keine Möglichkeit, ein Mapping-Objekt (z. B. ein Dictionary) als Parameter zu übergeben und es auf die gleiche Weise entpacken zu lassen, wie eine Sequenz in einen Tupelparameter.
Uninformative Fehlermeldungen
Betrachten Sie die folgende Funktion
def fxn((a, b), (c, d)):
pass
Wenn sie als fxn(1, (2, 3)) aufgerufen wird, erhält man die Fehlermeldung TypeError: unpack non-sequence. Diese Fehlermeldung gibt keinerlei Auskunft darüber, welches Tupel nicht richtig entpackt wurde. Es gibt auch keinen Hinweis darauf, dass dies ein Ergebnis war, das aufgrund der Argumente aufgetreten ist. Andere Fehlermeldungen bezüglich Funktionsargumenten geben explizit ihre Beziehung zur Signatur an: TypeError: fxn() takes exactly 2 arguments (0 given), etc.
Wenig Nutzung
Obwohl eine informelle Umfrage unter einer Handvoll von Python-Programmierern, die ich persönlich kenne, und vom PyCon 2007 Sprint eine riesige Mehrheit anzeigt, dass die Leute von diesem Merkmal nichts wissen und der Rest es einfach nicht benutzt, werden einige harte Zahlen benötigt, um die Behauptung zu untermauern, dass das Merkmal nicht stark genutzt wird.
Durchlaufen jeder Zeile im Code-Repository von Python im Verzeichnis Lib/ mit dem regulären Ausdruck ^\s*def\s*\w+\s*\(, um Funktions- und Methodendefinitionen zu erkennen, gab es 22.252 Übereinstimmungen im Trunk.
Hinzufügen von .*,\s*\(, um def-Anweisungen zu finden, die einen Tupelparameter enthielten, ergaben nur 41 Übereinstimmungen. Das bedeutet, dass für def-Anweisungen nur 0,18 % davon einen Tupelparameter zu verwenden scheinen.
Warum sie (angeblich) bleiben sollten
Praktischer Nutzen
In bestimmten Fällen können Tupelparameter nützlich sein. Ein gängiges Beispiel ist Code, der ein zweielementiges Tupel erwartet, das einen kartesischen Punkt darstellt. Obwohl es richtig ist, dass es schön ist, die Entpackung der x- und y-Koordinaten für Sie zu ermöglichen, ist das Argument, dass dieser geringe praktische Nutzen stark von anderen Problemen im Zusammenhang mit Tupelparametern überschattet wird. Und wie in Kein Verlust von Fähigkeiten bei Entfernung gezeigt, ist ihre Verwendung rein praktisch und bietet keinerlei einzigartige Fähigkeit, die nicht auf andere Weise sehr einfach gehandhabt werden kann.
Selbst-Dokumentation für Parameter
Es wurde argumentiert, dass Tupelparameter eine Möglichkeit zur Selbst-Dokumentation für Parameter bieten, die von einem bestimmten Sequenzformat sein sollen. Unter Verwendung unseres kartesischen Punktbeispiels aus Praktischer Nutzen macht das Sehen von (x, y) als Parameter in einer Funktion offensichtlich, dass ein Tupel der Länge zwei als Argument für diesen Parameter erwartet wird.
Aber Python bietet mehrere andere Möglichkeiten, zu dokumentieren, wofür Parameter gedacht sind. Dokumentationszeichenfolgen sollen genügend Informationen liefern, um zu erklären, welche Argumente erwartet werden. Tupelparameter können Ihnen die erwartete Länge eines Sequenzarguments mitteilen, aber sie sagen Ihnen nicht, wofür diese Daten verwendet werden. Man muss auch die Docstring lesen, um zu erfahren, welche anderen Argumente erwartet werden, wenn nicht alle Parameter Tupelparameter sind.
Funktionsannotationen (die nicht mit Tupelparametern funktionieren) können ebenfalls Dokumentation liefern. Da Annotationen jede Form haben können, kann ein früherer Tupelparameter ein einzelner Argumentparameter mit der Annotation tuple, tuple(2), Cartesian point, (x, y), etc. sein. Annotationen bieten große Flexibilität bei der Dokumentation dessen, wofür ein Argument für einen Parameter erwartet wird, einschließlich der Sequenz einer bestimmten Länge.
Migrationsplan
Um Python 2.x-Code auf 3.x zu portieren, wo Tupelparameter entfernt werden, werden zwei Schritte vorgeschlagen. Erstens wird die entsprechende Warnung ausgegeben, wenn der Compiler von Python auf einen Tupelparameter in Python 2.6 stößt. Dies wird wie jede andere syntaktische Änderung behandelt, die in Python 3.0 im Vergleich zu Python 2.6 stattfinden wird.
Zweitens wird das 2to3-Refactoring-Tool [1] einen Fixer [2] erhalten, um Tupelparameter in einen einzelnen Parameter umzuwandeln, der als erste Anweisung in der Funktion entpackt wird. Der Name des neuen Parameters wird geändert. Der neue Parameter wird dann in die Namen entpackt, die ursprünglich im Tupelparameter verwendet wurden. Das bedeutet, dass die folgende Funktion
def fxn((a, (b, c))):
pass
wird übersetzt in
def fxn(a_b_c):
(a, (b, c)) = a_b_c
pass
Da Tupelparameter von Lambdas verwendet werden, da diese eine einzige Ausdrucksbeschränkung haben, müssen sie ebenfalls unterstützt werden. Dies geschieht, indem das erwartete Sequenzargument einem einzelnen Parameter zugewiesen und dann über diesen Parameter indiziert wird
lambda (x, y): x + y
wird übersetzt in
lambda x_y: x_y[0] + x_y[1]
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-3113.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT