[pyar] La función a la que se le mueve la estantería

Federico Gonzalez federicogonzalez761 en gmail.com
Vie Jun 7 17:42:23 -03 2019


En el caso de la segunda función, yo entiendo que pasa esto:

funcs = []
for i in range(3):
    def foo(n, x=i):
        return n ** x
    funcs.append(foo)

de hecho lo podes probar porque si haces funcs[0](7, algo), te da el 7 **
algo (en el funcs = [lambda n, i=i: n ** i for i in range(3)]).

El vie., 7 de jun. de 2019 a la(s) 17:36, Matías Bellone (
matiasbellone en gmail.com) escribió:

> On Fri, Jun 7, 2019 at 4:56 PM Facundo Batista <facundobatista en gmail.com>
> wrote:
>
>> Hola!
>>
>> Este efecto es conocido:
>>
>> >>> funcs = [lambda n: n ** i for i in range(3)]
>> >>> funcs[0](7)
>> 49
>>
>> Sé como solucionarlo:
>>
>> >>> funcs = [lambda n, i=i: n ** i for i in range(3)]
>> >>> funcs[0](7)
>> 1
>>
>> Pero me interesa escarbar un poquito en por qué sucede. Alguien sabe?
>> Idea de por dónde buscar?
>>
>
> Originalmente pensé en utilizar ast (disclaimer: primera vez que lo uso)
> para ver si por ahí venía la mano:
>
> >>> import ast
> >>> a = ast.parse('f = [lambda n: n ** i for i in range(3)]')
> >>> ast.dump(a)
> "Module(body=[Assign(targets=[Name(id='f', ctx=Store())],
> value=ListComp(elt=Lambda(args=arguments(args=[arg(arg='n',
> annotation=None)], vararg=None, kwonlyargs=[], kw_defaults=[], kwarg=None,
> defaults=[]), body=BinOp(left=Name(id='n', ctx=Load()), op=Pow(),
> right=Name(id='i', ctx=Load()))),
> generators=[comprehension(target=Name(id='i', ctx=Store()),
> iter=Call(func=Name(id='range', ctx=Load()), args=[Num(n=3)], keywords=[]),
> ifs=[], is_async=0)]))])"
> >>> b = ast.parse('f = [lambda n, i=i: n ** i for i in range(3)]')
> >>> ast.dump(b)
> "Module(body=[Assign(targets=[Name(id='f', ctx=Store())],
> value=ListComp(elt=Lambda(args=arguments(args=[arg(arg='n',
> annotation=None), arg(arg='i', annotation=None)], vararg=None,
> kwonlyargs=[], kw_defaults=[], kwarg=None, defaults=[Name(id='i',
> ctx=Load())]), body=BinOp(left=Name(id='n', ctx=Load()), op=Pow(),
> right=Name(id='i', ctx=Load()))),
> generators=[comprehension(target=Name(id='i', ctx=Store()),
> iter=Call(func=Name(id='range', ctx=Load()), args=[Num(n=3)], keywords=[]),
> ifs=[], is_async=0)]))])"
>
> Escribiendo el mail me dí cuenta que mi intento de explicación no tenía
> sentido sólo viendo el AST así que pongo lo que se me ocurre que puedo
> deducir de lo que ahí dice. La única diferencia es defaults en el Lambda,
> por lo que probablemente la ejecución de esa parte del AST genere una copia
> local de i en el contexto de ese Lambda en lugar de utilizar el global a
> resolver dentro de BinOp. Algo análogo a lo que pasa cuando utilizás
> objetos mutables como default en la definición de una función.
>
> O eso me parece.
>
> Saludos,
> Toote
> _______________________________________________
> Lista de Correo de PyAr - Python Argentina - pyar en python.org.ar
> Sitio web: http://www.python.org.ar/
>
> Para administrar la lista (o desuscribirse) entrar a
> http://listas.python.org.ar/listinfo/pyar
>
> La lista de PyAr esta Hosteada en USLA - Usuarios de Software Libre de
> Argentina - http://www.usla.org.ar
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://listas.python.org.ar/pipermail/pyar/attachments/20190607/56955256/attachment.html>


Más información sobre la lista de distribución pyar