Entpacken des Python-Tupels in der return-Anweisung

76

Die Python-Sprache (insbesondere 3.x) ermöglicht das sehr allgemeine Entpacken von Iterables, ein einfaches Beispiel dafür

a, *rest = 1, 2, 3

Im Laufe der Jahre wurde dieses Auspacken schrittweise verallgemeinert (siehe z. B. PEP 3132 und PEP 448 ), so dass es unter immer mehr Umständen verwendet werden kann. Daher war ich überrascht zu entdecken, dass die folgende Syntax in Python 3.6 ungültig ist (und dies auch in Python 3.7 bleibt):

def f():
    rest = [2, 3]
    return 1, *rest  # Invalid

Ich kann es zum Laufen bringen, indem ich das zurückgegebene Tupel wie folgt in Klammern kapsle:

def f():
    rest = [2, 3]
    return (1, *rest)  # Valid

Die Tatsache, dass ich dies in einer returnAussage verwende, scheint wichtig zu sein

t = 1, *rest

ist in der Tat legal und führt zum gleichen mit und ohne Klammern.

Wurde dieser Fall von den Python-Entwicklern einfach vergessen, oder gibt es einen Grund, warum dieser Fall eine ungültige Syntax ist?

Warum es mich interessiert

Dies bricht einen wichtigen Vertrag, den ich mit der Python-Sprache zu haben glaubte. Betrachten Sie die folgende (auch gültige) Lösung:

def f():
    rest = [2, 3]
    t = 1, *rest
    return t

Wenn ich Code wie diesen habe, halte ich ihn normalerweise tfür einen temporären Namen, den ich loswerden sollte, indem ich ihn einfach tin der unteren Zeile durch seine Definition ersetze . In diesem Fall führt dies jedoch zu einem ungültigen Code

def f():
    rest = [2, 3]
    return 1, *rest

Es ist natürlich keine große Sache, Klammern um den Rückgabewert setzen zu müssen, aber normalerweise werden zusätzliche Klammern nur benötigt, um zwischen mehreren möglichen Ergebnissen zu unterscheiden (Gruppierung). Hier ist dies nicht der Fall, da das Weglassen der Klammern kein anderes unerwünschtes Verhalten hervorruft, sondern überhaupt kein Verhalten.

Aktualisieren

Seit Python 3.8 (siehe Punkt 7 in dieser Liste ) ist die oben beschriebene verallgemeinerte Syntax jetzt gültig.

jmd_dk
quelle
4
Dies ist wirklich mehr eine Folge der Grammatiksyntax als alles andere.
CS95
Sie können auch nicht einfach * rest zurückgeben, es ist eine ungültige Syntax.
Lapisdecor
3
@lapisdecor Ja, aber das stimmt mit der Tatsache überein, dass t = *restes ungültig ist. Auch return *restund t = *reststellt kein tatsächliches Auspacken dar, so dass ich es nicht als Problem empfinde, dass dies nicht erlaubt ist. Wenn es erlaubt wäre, *restwäre allein nur eine verwirrende Syntax für tuple(rest).
jmd_dk
Dies geschieht mit mehr als nur return. Das Auspacken ist auch in einem yieldArgument, einem Index, der RHS einer erweiterten Zuordnung (aber nicht einer regulären Zuordnung) und rechts von der inin einer forAnweisung verboten, obwohl in all diesen Positionen nicht parenthesierte Tupel zulässig sind, da die Syntax für diese Dinge verwendet expression_liststatt starred_expression.
user2357112 unterstützt Monica
3
Beachten Sie den Unterschied zwischen t = *restund t = *rest,. Letzteres ist gültig.
senderle

Antworten:

38

Ich vermute, dass dies ein Unfall ist, basierend auf den Kommentaren aus diesem Commit für Python 3.2.

Dieses Commit ermöglichte es dem Zuweisungsausdruck, eine testlist_star_exprProduktion aufzunehmen (was das nicht entpackte Entpacken ermöglicht), ließ jedoch die return-Anweisung eine testlistProduktion übernehmen. Ich vermute, dass das Commit dies gerade verpasst hat (und möglicherweise auch an anderen Orten, aber ich konzentriere mich vorerst auf die return_stmtProduktion).

Ich habe die Python-Grammatik / Grammatik-Datei geändert, um dies zu ermöglichen. Alle Tests bestehen weiterhin, einschließlich der in der test_grammar.pyDatei enthaltenen (dies scheint jedoch nicht besonders erschöpfend zu sein).

Wenn Sie neugierig sind, ist dies die Änderung, die ich vorgenommen habe . Fühlen Sie sich frei, meine Gabel zu klonen oder herunterzuladen .

UPDATE: Ich habe ein bpo-Problem und eine Pull-Anfrage für das Auspacken von Rückgabe (und Ertrag) eingereicht .

David Cuthbert
quelle
3
Haben Sie mit dieser Änderung bereits eine Pull-Anfrage gestellt?
Martijn Pieters
Noch nicht. Wollte sehen, ob dies das Problem von @jmd_dk richtig löst, und vielleicht ein paar andere Fälle (wie die yield_stmtProduktion) betrachten, bevor ich es weitersende .
David Cuthbert
2
Das Erstellen eines 'Fixes' scheint etwas ausgereift zu sein. Senden
Chris_Rands
3
Das Update soll in Python 3.8
Michael Scott Cuthbert