5 ms·
This is your first snippet (expanded to two lines and max index reduced from 5 to 3): lambda_list = [lambda: i for i in range(3)] results = [x() for x in
by quietbritishjim 3y ago
This is your first snippet (expanded to two lines and max index reduced from 5 to 3):
lambda_list = [lambda: i for i in range(3)]
results = [x() for x in lambda_list]
It expands to this:
i = 0
f0 = lambda: i
i = 1
f1 = lambda: i
i = 2
f2 = lambda: i
results = [f0(), f1(), f2()]
The body of those functions f0, f1, f2, each looks in the function's own dictionary of local variables for "i". But it doesn't exist there, so it looks in the enclosing scope (and keeps doing this recursively up to global scope if necessary). At the next scope it does find a variable "i". This happens at the point you call the function (the same as anything else inside the definition of a function), in this case on that last line when you assign to results. By that point, the "i" variable in the enclosing scope has value 2. So results = [2, 2, 2].
[edit: this feature of functions in Python (variables are looked up by name when the function is run) is what lets you define functions foo() and bar() in a module / script that call each other. When the first one is defined, the other one doesn't exist yet, but you're not actually running the code inside the function so it doesn't matter. By the time you come to call it later, the other function does exist in the enclosing (module level) scope so the lookup succeeds.]
This is your second snippet (I changed one of the variable names from i to j for clarity, but you can rename it back and it would work the same way):
lambda_list = [lambda j=i: j for i in range(3)]
results = [x() for x in lambda_list]
it expands like this:
i = 0
f0 = lambda j=i: j
i = 1
f1 = lambda j=i: j
i = 2
f2 = lambda j=i: j
results = [f0(), f1(), f2()]
But defaults of function arguments are evaluated at the point the function is declared, not when it's called (as noted elsewhere in this thread - hence the def foo(x=[]) gotcha!). You can imagine the second line of code above is like this, just to really drive the point home [edit: I originally used a made up syntax, but I updated to code that actually works]:
f0 = lambda j: j
f0.__defaults__ = i,
f0.__kwdefaults__ = {"j": i}
So, f0 has the specific value 0 baked into it, and similarly f1 and f2 have 1 and 2. Therefore results = [0, 1, 2].
- acqq 3y agoI've tried to see __defaults__ and __kwdefaults__ here: i = 7 lambda_list = [lambda: i for i in range(3)] results = [x() for x in lambda_list] print( i ) for k in range( 3 ): print( lambda_list[k] ) print( lambda_list[k].__defaults__ ) print( lambda_list[k].__kwdefaults__ ) lambda_list = [lambda j=i: j for i in range(3)] results = [x() for x in lambda_list] print( i ) for k in range( 3 ): print( lambda_list[k] ) print( lambda_list[k].__defaults__ ) print( lambda_list[k].__kwdefaults__ ) print( i ) The output is: 7 <function <listcomp>.<lambda> at 0xB2> None None <function <listcomp>.<lambda> at 0xBC> None None <function <listcomp>.<lambda> at 0xC6> None None 7 <function <listcomp>.<lambda> at 0xD0> (0,) None <function <listcomp>.<lambda> at 0xDA> (1,) None <function <listcomp>.<lambda> at 0xE4> (2,) None 7 If I understand it, __kwdefaults__ are always None and the __defaults__ used only in the second case. I've also tried to see the "i" as not being inside of the closure, but it remains 7, not like (as noted by 'remram here): i = 9 y = 1 for y in range( 1,-1,-1 ): try: x = 4 / y except Exception as i: pass print( i ) Which gives, for me, fascinating: 9 Traceback (most recent call last): File "p.py", line 10, in <module> print( i ) ^ NameError: name 'i' is not defined. Did you mean: 'id'? I'm learning a lot here.
- quietbritishjim 3y agoOops, `__kwdefaults__` is only for keyword-only arguments fn = lambda x, *args, y=3: 0 print(fn.__defaults__, fn.__kwdefaults__) # prints None {'y': 3} Source: https://stackoverflow.com/questions/17533929/what-is-the-use-of-kwdefaults-which-is-a-function-object-attribute https://stackoverflow.com/questions/17533929/what-is-the-use...