Generator jako argument funkcji
Czy ktoś może wyjaśnić, dlaczego przekazywanie generatora jako jedynego argumentu pozycyjnego do funkcji wydaje się mieć specjalne reguły?
Jeśli mamy:
>>> def f(*args):
>>> print "Success!"
>>> print args
-
To działa 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
-
To działa, 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 to nie powinno zawieść w taki sam sposób jak 2)
>>> f(*[2], 1 for x in [1]) Success! (generator object <genexpr> at 0x7effe06bdcd0>, 2)
1 answers
Oba 3. i 4. powinny być błędami składni we wszystkich wersjach Pythona. jednak znalazłeś błąd, który wpływa na wersje Pythona 2.5 - 3.4, a który został następnie wysłany do śledzenia problemów Pythona . Z powodu błędu, nieparentyfikowane wyrażenie generatora zostało przyjęte jako argument funkcji, jeśli towarzyszyły mu tylko *args
i / lub **kwargs
. Podczas gdy Python 2.6 + dopuszczał oba przypadki 3. i 4., Python 2.5 dozwolony tylko przypadek 3. - ale obaj byli wobec udokumentowanej gramatyki :
call ::= primary "(" [argument_list [","]
| expression genexpr_for] ")"
Tzn. dokumentacja mówi, że wywołanie funkcji składa się z primary
(wyrażenia, które ewaluuje do wywołania), a następnie, w nawiasach, albo {[33] } listy argumentów lub po prostu nieparentowanego wyrażenia generatora;
a w obrębie listy argumentów wszystkie wyrażenia generatora muszą znajdować się w nawiasach.
Ten błąd (choć wydaje się, że nie był znany), został naprawiony w Pythonie 3.5 prerelease. W 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 Detererowi zauważającemu ten błąd.
Analiza błędu
Nastąpiła zmiana w Pythonie 2.6, która pozwoliła na użycie argumentów słów kluczowych Po *args
:
Legalne staje się również podawanie argumentów słów kluczowych po a * args argument 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}
Poprzednio był to błąd składni. (Dodany przez Amaury Forgeot d ' Arc; wydanie 3473.)
Gramatyka Pythona 2.6 nie rozróżnia argumentów słów kluczowych, argumentów pozycyjnych lub wyrażeń generatora - wszystkie są typu argument
dla parsera.
Zgodnie z regułami Pythona, wyrażenie generatora musi być w nawiasie, jeśli nie jest jedynym argumentem do 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 bierze pod uwagę w ogóle - w szczególności szuka tylko zwykłych argumentów pozycyjnych i argumentów słów kluczowych.
Dalej w tej samej funkcji generowany jest komunikat o błędzie dla Nie-słowa kluczowego arg po słowie kluczowym arg :
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 są a nie wyrażeniami generatora jako else if
} twierdzenie :
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 nieparentowane wyrażenie generatora mogło się poślizgnąć.
Teraz w Pythonie 3.5 Można używać *args
wszędzie w wywołaniu funkcji, więc
Gramatyka została zmieniona w celu dostosowania do tego:
arglist: argument (',' argument)* [',']
I
argument: ( test [comp_for] |
test '=' test |
'**' test |
'*' test )
I 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 poprawne spojrzenie konstrukcje
func(i for i in [42], *args)
I
func(i for i in [42], **kwargs)
Gdzie nieparentowany generator poprzedza *args
lub **kwargs
teraz przestał działać.
Aby zlokalizować ten błąd, wypróbowałem różne wersje 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 to zostało naprawione przed jakąś wersją 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
W Pythonie 3.5, ale nie działa nie w Pythonie 3.4:
f(*[1], (2 for x in [2]))
I to jest wskazówka. W Python 3.5 *splatting
jest uogólniony; można go używać wszędzie w wywołaniu funkcji:
>>> print(*range(5), 42)
0 1 2 3 4 42
Tak więc rzeczywisty błąd (generator pracujący z *star
bez nawiasów) był rzeczywiście naprawiony w Pythonie 3.5, A błąd można znaleźć w tym, co zmieniło się między Pythonem 3.4 i 3.5
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-15 13:29:55