what used generators generadores for are python python-2.7 syntax generator-expression

generadores - what are python generators used for



Generador como argumento de funciĆ³n (1)

Tanto 3. como 4. deben ser errores de sintaxis en todas las versiones de Python. Sin embargo, ha encontrado un error que afecta a las versiones de Python 2.5 - 3.4, y que posteriormente se publicó en el rastreador de problemas de Python . Debido al error, se aceptaba una expresión generadora sin paréntesis como argumento de una función si solo estaba acompañada por *args y / o **kwargs . Mientras que Python 2.6+ permitió ambos casos 3. y 4., Python 2.5 permitió solo 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 que una llamada a la función se compone de primary (la expresión que se evalúa como invocable), seguida de, entre paréntesis, una lista de argumentos o simplemente una expresión generadora sin paréntesis; y dentro de la lista de argumentos, todas las expresiones generadoras deben estar entre paréntesis.

Este error (aunque parece que no se conocía), se ha solucionado en Python 3.5 prereleases. 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 en 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 :

También es legal proporcionar argumentos de palabras clave después de un argumento * args a una llamada de función.

>>> def f(*args, **kw): ... print args, kw ... >>> f(1,2,3, *(4,5,6), keyword=13) (1, 2, 3, 4, 5, 6) {''keyword'': 13}

Anteriormente, esto habría sido un error de sintaxis. (Contribución de Amaury Forgeot d''Arc; número 3473.)

Sin embargo, la grammar Python 2.6 no distingue entre argumentos de palabras clave, argumentos posicionales o expresiones de generador desnudas; todos son argument de tipo 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 se valida 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 considera en absoluto los argumentos *args , solo busca específicamente argumentos posicionales y argumentos de palabras clave.

Más abajo en la misma función, se genera un mensaje de error para el argumento sin 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 se aplica nuevamente a los argumentos que no son expresiones generadoras sin paréntesis como lo demuestra la declaración else if :

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 lo tanto, se dejó pasar una expresión de generador sin paréntesis.

Ahora en Python 3.5 uno puede usar los *args en cualquier parte de una llamada de función, por lo que la Grammar se cambió para adaptarse a esto:

arglist: argument ('','' argument)* ['','']

y

argument: ( test [comp_for] | test ''='' test | ''**'' test | ''*'' test )

y el bucle for se cambió a

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++; } }

Así arreglando el error.

Sin embargo, el cambio involuntario 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 a *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 una 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 del generador 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 de una llamada de función:

>>> print(*range(5), 42) 0 1 2 3 4 42

Entonces, el error real (generador que funciona con *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.5

¿Alguien puede explicar por qué pasar un generador como el único argumento posicional de una función parece tener reglas especiales?

Si tenemos:

>>> def f(*args): >>> print "Success!" >>> print args

  1. Esto funciona, como se esperaba.

    >>> f(1, *[2]) Success! (1, 2)

  2. Esto no funciona, como se esperaba.

    >>> f(*[2], 1) File "<stdin>", line 1 SyntaxError: only named arguments may follow *expression

  3. Esto funciona, como se esperaba

    >>> f(1 for x in [1], *[2]) Success! (generator object <genexpr> at 0x7effe06bdcd0>, 2)

  4. 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)