Sowohl 3. als auch 4. sollten in allen Python-Versionen Syntaxfehler sein. Sie haben jedoch einen Fehler gefunden, der die Python-Versionen 2.5 - 3.4 betrifft und der anschließend im Python-Issue-Tracker veröffentlicht wurde . Aufgrund des Fehlers wurde ein nicht in Klammern stehender Generatorausdruck als Argument für eine Funktion akzeptiert, wenn er nur von *args
und / oder begleitet wurde **kwargs
. Während Python 2.6+ beide Fälle 3. und 4. erlaubte, erlaubte Python 2.5 nur Fall 3. - doch beide waren gegen die dokumentierte Grammatik :
call ::= primary "(" [argument_list [","]
| expression genexpr_for] ")"
dh die Dokumentation sagt ein Funktionsaufruf besitzt primary
(der Ausdruck, der zu einer auswertet aufrufbaren), gefolgt von der in Klammern entweder einer Argumentliste oder nur ein unparenthesized Generator Ausdruck; und innerhalb der Argumentliste müssen alle Generatorausdrücke in Klammern stehen.
Dieser Fehler (obwohl er anscheinend nicht bekannt war) wurde in Python 3.5-Vorabversionen behoben. In Python 3.5 sind immer Klammern um einen Generatorausdruck erforderlich, es sei denn, dies ist das einzige Argument für die Funktion:
Python 3.5.0a4+ (default:a3f2b171b765, May 19 2015, 16:14:41)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f(1 for i in [42], *a)
File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument
Dies ist jetzt in den Neuigkeiten in Python 3.5 dokumentiert , da DeTeReR diesen Fehler entdeckt hat.
Analyse des Fehlers
In Python 2.6 wurde eine Änderung vorgenommen, die die Verwendung von Schlüsselwortargumenten nach *args
folgenden Bedingungen ermöglichte :
Es ist auch legal geworden, Schlüsselwortargumente nach einem * args-Argument für einen Funktionsaufruf bereitzustellen.
>>> def f(*args, **kw):
... print args, kw
...
>>> f(1,2,3, *(4,5,6), keyword=13)
(1, 2, 3, 4, 5, 6) {'keyword': 13}
Bisher war dies ein Syntaxfehler. (Beitrag von Amaury Forgeot d'Arc; Ausgabe 3473.)
Die Python 2.6- Grammatik unterscheidet jedoch nicht zwischen Schlüsselwortargumenten, Positionsargumenten oder bloßen Generatorausdrücken - sie sind alle vom Typ argument
für den Parser.
Gemäß den Python-Regeln muss ein Generatorausdruck in Klammern gesetzt werden, wenn dies nicht das einzige Argument für die Funktion ist. Dies wird validiert in Python/ast.c
:
for (i = 0; i < NCH(n); i++) {
node *ch = CHILD(n, i);
if (TYPE(ch) == argument) {
if (NCH(ch) == 1)
nargs++;
else if (TYPE(CHILD(ch, 1)) == gen_for)
ngens++;
else
nkeywords++;
}
}
if (ngens > 1 || (ngens && (nargs || nkeywords))) {
ast_error(n, "Generator expression must be parenthesized "
"if not sole argument");
return NULL;
}
Allerdings ist diese Funktion nicht die betrachtet *args
überhaupt - es speziell schaut nur für gewöhnliche Positionsargumente und Keyword - Argumente.
Weiter unten in derselben Funktion wird eine Fehlermeldung für Nicht-Schlüsselwort arg nach Schlüsselwort arg generiert :
if (TYPE(ch) == argument) {
expr_ty e;
if (NCH(ch) == 1) {
if (nkeywords) {
ast_error(CHILD(ch, 0),
"non-keyword arg after keyword arg");
return NULL;
}
...
Dies gilt jedoch wiederum für Argumente, bei denen es sich nicht um nicht parästhesierte Generatorausdrücke handelt, wie aus der else if
Aussage hervorgeht :
else if (TYPE(CHILD(ch, 1)) == gen_for) {
e = ast_for_genexp(c, ch);
if (!e)
return NULL;
asdl_seq_SET(args, nargs++, e);
}
Somit wurde ein nicht parästhesierter Generatorausdruck passieren gelassen.
In Python 3.5 kann man jetzt die *args
beliebige Stelle in einem Funktionsaufruf verwenden, daher wurde die Grammatik geändert, um dies zu berücksichtigen:
arglist: argument (',' argument)* [',']
und
argument: ( test [comp_for] |
test '=' test |
'**' test |
'*' test )
und die for
Schleife wurde geändert in
for (i = 0; i < NCH(n); i++) {
node *ch = CHILD(n, i);
if (TYPE(ch) == argument) {
if (NCH(ch) == 1)
nargs++;
else if (TYPE(CHILD(ch, 1)) == comp_for)
ngens++;
else if (TYPE(CHILD(ch, 0)) == STAR)
nargs++;
else
/* TYPE(CHILD(ch, 0)) == DOUBLESTAR or keyword argument */
nkeywords++;
}
}
So wird der Fehler behoben.
Die unbeabsichtigte Änderung ist jedoch, dass die Konstruktionen gültig aussehen
func(i for i in [42], *args)
und
func(i for i in [42], **kwargs)
wo ein nicht parästhesierter Generator vorausgeht *args
oder **kwargs
jetzt nicht mehr funktioniert.
Um diesen Fehler zu finden, habe ich verschiedene Python-Versionen ausprobiert. In 2.5 erhalten Sie SyntaxError
:
Python 2.5.5 (r255:77872, Nov 28 2010, 16:43:48)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> f(*[1], 2 for x in [2])
File "<stdin>", line 1
f(*[1], 2 for x in [2])
Und dies wurde vor einer Vorabversion von Python 3.5 behoben:
Python 3.5.0a4+ (default:a3f2b171b765, May 19 2015, 16:14:41)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> f(*[1], 2 for x in [2])
File "<stdin>", line 1
SyntaxError: Generator expression must be parenthesized if not sole argument
Der Generatorausdruck in Klammern funktioniert jedoch in Python 3.5, jedoch nicht in Python 3.4:
f(*[1], (2 for x in [2]))
Und das ist der Hinweis. In Python 3.5 *splatting
ist das verallgemeinert; Sie können es überall in einem Funktionsaufruf verwenden:
>>> print(*range(5), 42)
0 1 2 3 4 42
Der eigentliche Fehler (Generator, der *star
ohne Klammern arbeitet) wurde tatsächlich in Python 3.5 behoben, und der Fehler konnte darin gefunden werden, was sich zwischen Python 3.4 und 3.5 geändert hat
f((*[2, 3]), 1)
gibt Syntaxfehler bei*
- könnten Sie bitte Ihren Vorschlag weiter erläutern? Die Frage ist auch nicht "wie es funktioniert", sondern "warum funktioniert es so?"