Czy ktoś może wyjaśnić, dlaczego przekazywanie generatora jako jedynego argumentu pozycyjnego funkcji wydaje się mieć specjalne reguły?
Jeśli mamy:
def f(*args):
print "Success!"
print args
Działa to zgodnie z oczekiwaniami.
>>> f(1, *[2]) Success! (1, 2)
To nie działa, zgodnie z oczekiwaniami.
>>> f(*[2], 1) File "<stdin>", line 1 SyntaxError: only named arguments may follow *expression
Działa to zgodnie z oczekiwaniami
>>> f(1 for x in [1], *[2]) Success! (generator object <genexpr> at 0x7effe06bdcd0>, 2)
To działa, ale nie rozumiem dlaczego. Czy nie powinno zawieść w taki sam sposób, jak 2)
>>> f(*[2], 1 for x in [1]) Success! (generator object <genexpr> at 0x7effe06bdcd0>, 2)
python
python-2.7
syntax
generator-expression
DeTeReR
źródło
źródło
Odpowiedzi:
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
: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ą typu
argument
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 nafor (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źródło
f(*[1], 1 for x in [1])
=>(<generator object <genexpr> at 0x7fa56c889288>, 1)
f(*[1], (1 for x in [1]))
to błąd składni w Pythonie 3.4. Działa w Pythonie 3.5.