3. et 4. devrait être des erreurs de syntaxe sur toutes les versions de Python. Cependant, vous avez trouvé un bug qui affecte les versions de Python 2.5 - 3.4, et qui a par la suite été publié pour le Python issue tracker. En raison du bogue, sans parenthèse générateur d'expression a été accepté comme un argument à une fonction, si elle a été accompagnée que par *args
et/ou **kwargs
. Alors que la version 2.6 de Python+ a permis à la fois de cas 3. et 4., Python 2.5 autorisé seulement 3 de la casse. - pourtant, les deux d'entre eux étaient contre le documentée de la grammaire:
call ::= primary "(" [argument_list [","]
| expression genexpr_for] ")"
c'est à dire la documentation dit d'un appel de fonction se compose d' primary
(l'expression qui correspond à un callable), suivie, entre parenthèses, soit une liste d'arguments ou juste un sans parenthèse générateur d'expression;
et dans la liste d'arguments, tous générateur d'expressions entre parenthèses.
Ce bug (mais il semble qu'il n'avait pas été connu), avait été fixé en Python 3.5 prereleases. En Python 3.5 parenthèses sont toujours nécessaires autour d'un générateur d'expression, à moins qu'il est le seul argument à la fonction:
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
C'est maintenant documenté dans les nouveautés de Python 3.5, grâce à DeTeReR repérage de ce bug.
L'analyse de la bogue
Il n'y avait qu'une modification apportée à la version 2.6 de Python qui a permis l'utilisation du mot-clé arguments après *args
:
Il est également devenu morale de fournir des arguments mot-clé après un *args
argument d'un appel de fonction.
>>> def f(*args, **kw):
... print args, kw
...
>>> f(1,2,3, *(4,5,6), keyword=13)
(1, 2, 3, 4, 5, 6) {'keyword': 13}
Auparavant, cela aurait été une erreur de syntaxe. (Contribué par Amaury
Forgeot d'Arc; question 3473.)
Cependant, la version 2.6 de Python de la grammaire ne fait aucune distinction entre le mot-clé arguments, les arguments de position, ou nu générateur d'expressions - ils sont tous de type argument
de l'analyseur.
Comme par Python règles, un générateur d'expression doit être mise entre parenthèses, si ce n'est pas le seul argument à la fonction. C'est validé dans l' 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;
}
Cependant, cette fonction ne pas considérer l' *args
- plus particulièrement, elle ne regarde que pour le commun des arguments de position et les arguments mots-clefs.
Plus bas dans la même fonction, il y a un message d'erreur généré par la non-mot-clé arg après le mot-clé 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;
}
...
Mais cela s'applique à nouveau à des arguments qui ne sont pas sans parenthèse générateur d'expressions comme en témoigne l' else if
déclaration:
else if (TYPE(CHILD(ch, 1)) == gen_for) {
e = ast_for_genexp(c, ch);
if (!e)
return NULL;
asdl_seq_SET(args, nargs++, e);
}
Donc un sans parenthèse générateur d'expression a été autorisé à se glisser passer.
Maintenant en Python 3.5, on peut utiliser le *args
n'importe où dans un appel de fonction, donc
la Grammaire a été modifié pour tenir compte de ceci:
arglist: argument (',' argument)* [',']
et
argument: ( test [comp_for] |
test '=' test |
'**' test |
'*' test )
et l' for
boucle a été changé à
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++;
}
}
Ce qui permet de corriger le bug.
Toutefois, la modification par inadvertance, c'est que la validité de la recherche de constructions
func(i for i in [42], *args)
et
func(i for i in [42], **kwargs)
lorsqu'un sans parenthèse générateur précède *args
ou **kwargs
maintenant cessé de travailler.
Pour trouver ce bug, j'ai essayé différentes versions de Python. Dans la version 2.5, vous obtiendrez 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])
Et cela a été fixée avant certains préliminaire de Python 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
Cependant, la mise entre parenthèses du générateur d'expression, il fonctionne en Python 3.5, mais il ne fonctionne pas en Python 3.4:
f(*[1], (2 for x in [2]))
Et c'est la moindre idée. En Python 3.5 *splatting
est généralisée; vous pouvez l'utiliser n'importe où dans un appel de fonction:
>>> print(*range(5), 42)
0 1 2 3 4 42
La bogue (générateur de travailler avec des *star
sans parenthèses) était en effet fixé en Python 3.5, et le bug a pu être trouvé en ce que ce qui a changé entre Python 3.4 et 3.5