¿Alguien puede explicar por qué pasar un generador como único argumento posicional a una función parece tener reglas especiales?
Si tenemos:
>>> def f(*args):
>>> print "Success!"
>>> print args
Esto funciona, como se esperaba.
>>> f(1, *[2]) Success! (1, 2)
Esto no funciona como se esperaba.
>>> f(*[2], 1) File "<stdin>", line 1 SyntaxError: only named arguments may follow *expression
Esto funciona, como se esperaba
>>> f(1 for x in [1], *[2]) Success! (generator object <genexpr> at 0x7effe06bdcd0>, 2)
Esto funciona, pero no entiendo por qué. ¿No debería fallar de la misma manera que 2)
>>> f(*[2], 1 for x in [1]) Success! (generator object <genexpr> at 0x7effe06bdcd0>, 2)
python
python-2.7
syntax
generator-expression
DeTeReR
fuente
fuente
f((*[2, 3]), 1)
da un error de sintaxis en*
- ¿podría explicar más su sugerencia? Además, la pregunta no es "cómo hacer que funcione", sino "¿por qué funciona así?"Respuestas:
Tanto 3. como 4. deberían ser errores de sintaxis en todas las versiones de Python. Sin embargo, ha encontrado un error que afecta a las versiones 2.5 - 3.4 de Python y que posteriormente se publicó en el rastreador de problemas de Python . Debido al error, se aceptó una expresión generadora sin paréntesis como argumento para una función si estaba acompañada solo por
*args
y / o**kwargs
. Mientras que Python 2.6+ permitía ambos casos 3. y 4., Python 2.5 solo permitía el caso 3. - sin embargo, ambos estaban en contra de la gramática documentada :call ::= primary "(" [argument_list [","] | expression genexpr_for] ")"
es decir, la documentación dice una llamada de función dispone de
primary
(la expresión que se evalúa como una opción de rescate), seguidos por, entre paréntesis, o bien una lista de argumentos o sólo una expresión generador unparenthesized; y dentro de la lista de argumentos, todas las expresiones del generador deben estar entre paréntesis.Este error (aunque parece que no se conocía), se había corregido en las versiones preliminares de Python 3.5. En Python 3.5 siempre se requieren paréntesis alrededor de una expresión generadora, a menos que sea el único argumento para la función:
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
Esto ahora está documentado en Novedades de Python 3.5 , gracias a que DeTeReR detectó este error.
Análisis del error
Se realizó un cambio en Python 2.6 que permitió el uso de argumentos de palabras clave después de
*args
:Sin embargo, la gramática de Python 2.6 no hace ninguna distinción entre argumentos de palabras clave, argumentos posicionales o expresiones generadoras simples; todos son de tipo
argument
para el analizador.Según las reglas de Python, una expresión generadora debe estar entre paréntesis si no es el único argumento de la función. Esto está validado en
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; }
Sin embargo, esta función no tiene en cuenta
*args
en absoluto; específicamente, solo busca argumentos posicionales ordinarios y argumentos de palabras clave.Más abajo en la misma función, hay un mensaje de error generado para arg que no son palabras clave después de la palabra clave 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; } ...
Pero esto nuevamente se aplica a argumentos que no son expresiones generadoras sin paréntesis, como lo demuestra la
else if
declaración :else if (TYPE(CHILD(ch, 1)) == gen_for) { e = ast_for_genexp(c, ch); if (!e) return NULL; asdl_seq_SET(args, nargs++, e); }
Por tanto, se permitió que pasara una expresión generadora sin paréntesis.
Ahora, en Python 3.5, se puede usar
*args
en cualquier lugar de una llamada de función, por lo que se cambió la gramática para adaptarse a esto:arglist: argument (',' argument)* [',']
y
argument: ( test [comp_for] | test '=' test | '**' test | '*' test )
y el
for
bucle se cambió afor (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++; } }
Arreglando así el error.
Sin embargo, el cambio inadvertido es que las construcciones de aspecto válido
func(i for i in [42], *args)
y
func(i for i in [42], **kwargs)
donde un generador sin paréntesis precede
*args
o**kwargs
ahora dejó de funcionar.Para localizar este error, probé varias versiones de Python. En 2.5 obtendrías
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])
Y esto se solucionó antes de alguna versión preliminar 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
Sin embargo, la expresión generadora entre paréntesis, funciona en Python 3.5, pero no funciona en Python 3.4:
f(*[1], (2 for x in [2]))
Y esta es la pista. En Python 3.5 el
*splatting
está generalizado; puede usarlo en cualquier lugar en una llamada de función:>>> print(*range(5), 42) 0 1 2 3 4 42
Entonces, el error real (el generador funciona
*star
sin paréntesis) se corrigió en Python 3.5, y el error se pudo encontrar en lo que cambió entre Python 3.4 y 3.5fuente
f(*[1], 1 for x in [1])
=>(<generator object <genexpr> at 0x7fa56c889288>, 1)
f(*[1], (1 for x in [1]))
es un error de sintaxis en Python 3.4. Es válido en Python 3.5.