Zarówno 3., jak i 4. powinny być błędami składniowymi we wszystkich wersjach Pythona. Jednak znalazłeś błąd, który dotyczy wersji Pythona 2.5 - 3.4 i który został następnie wysłany do modułu śledzenia problemów Pythona . Z powodu błędu nieprzedstawione wyrażenie generatora było akceptowane jako argument funkcji, jeśli towarzyszyło mu tylko *args
i / lub **kwargs
. Podczas gdy Python 2.6+ dopuszczał oba przypadki 3. i 4., Python 2.5 dopuszczał tylko przypadek 3. - ale oba były sprzeczne z udokumentowaną gramatyką :
call ::= primary "(" [argument_list [","]
| expression genexpr_for] ")"
czyli dokumentacja mówi Wywołanie funkcji składa się z primary
(wyrażenie, którego wynikiem jest wymagalne), a następnie, w nawiasie, albo na liście argumentów lub tylko unparenthesized ekspresji generatora; a na liście argumentów wszystkie wyrażenia generatora muszą znajdować się w nawiasach.
Ten błąd (choć wydaje się, że nie był znany), został naprawiony w wydaniach wstępnych Pythona 3.5. W Pythonie 3.5 nawiasy są zawsze wymagane wokół wyrażenia generatora, chyba że jest to jedyny argument funkcji:
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
Jest to teraz udokumentowane w Co nowego w Pythonie 3.5 , dzięki DeTeReR, który wykrył ten błąd.
Analiza błędu
Wprowadzono zmianę w Pythonie 2.6, która pozwoliła na użycie argumentów słów kluczowych po *args
:
Dozwolone jest również podawanie argumentów słów kluczowych po argumencie * args do wywołania funkcji.
>>> def f(*args, **kw):
... print args, kw
...
>>> f(1,2,3, *(4,5,6), keyword=13)
(1, 2, 3, 4, 5, 6) {'keyword': 13}
Wcześniej byłby to błąd składniowy. (Nadesłane przez Amaury Forgeot d'Arc; wydanie 3473.)
Jednak gramatyka Pythona 2.6 nie czyni żadnego rozróżnienia między argumentami słów kluczowych, argumentami pozycyjnymi lub zwykłymi wyrażeniami generatora - wszystkie są typuargument
samych dla parsera.
Zgodnie z regułami Pythona, wyrażenie generatora musi być umieszczone w nawiasach, jeśli nie jest jedynym argumentem funkcji. Jest to potwierdzone w 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;
}
Jednak ta funkcja nie uwzględnia*args
- w szczególności szuka tylko zwykłych argumentów pozycyjnych i argumentów słów kluczowych.
W dalszej części tej samej funkcji pojawia się komunikat o błędzie generowany dla argumentu innego niż słowo kluczowe po argumencie kluczowym :
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;
}
...
Ale to znowu odnosi się do argumentów, które nie są wyrażeniami generatora bez rodzicielstwa, czego dowodem jest else if
stwierdzenie :
else if (TYPE(CHILD(ch, 1)) == gen_for) {
e = ast_for_genexp(c, ch);
if (!e)
return NULL;
asdl_seq_SET(args, nargs++, e);
}
W ten sposób zezwolono na przejście przez nieprzedstawione wyrażenie generatora.
Teraz w Pythonie 3.5 można używać *args
dowolnego miejsca w wywołaniu funkcji, więc gramatyka została zmieniona, aby dostosować się do tego:
arglist: argument (',' argument)* [',']
i
argument: ( test [comp_for] |
test '=' test |
'**' test |
'*' test )
a for
pętla została zmieniona na
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++;
}
}
W ten sposób naprawiamy błąd.
Jednak nieumyślną zmianą jest to, że poprawnie wyglądające konstrukcje
func(i for i in [42], *args)
i
func(i for i in [42], **kwargs)
w przypadku, gdy generator, którego nie ma wcześniej, poprzedza *args
lub **kwargs
przestał działać.
Aby zlokalizować ten błąd, próbowałem różnych wersji Pythona. W 2.5 dostaniesz 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])
I zostało to naprawione przed premierą Pythona 3.5:
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
Jednak wyrażenie generatora w nawiasach działa w Pythonie 3.5, ale nie działa w Pythonie 3.4:
f(*[1], (2 for x in [2]))
I to jest wskazówka. W Pythonie 3.5 *splatting
jest to uogólnione; możesz go używać w dowolnym miejscu wywołania funkcji:
>>> print(*range(5), 42)
0 1 2 3 4 42
Tak więc rzeczywisty błąd (generator działający *star
bez nawiasów) został rzeczywiście naprawiony w Pythonie 3.5, a błąd można było znaleźć w tym, co zmieniło się między Pythonem 3.4 i 3.5