I want to define a lambda function inside another lambda function, but when I create the inner lambda function, the outer lambda function's argument is said to be undefined, like this.
# time, game, players... are defined above this line
result = filter(lambda x: (player == min(players, key=lambda y: time(game, y, x))), words)
Then, the x as one of the time function's argument is said to be an undefined variable. I searched through StackOverflow and found that I can use a default value to pass x into the scope of lambda y by changing it to lambda y, x=x: time(game, y, x).
I am wondering why x couldn't be used in the scope of lambda y directly, just like how game, time() are used in it. Aren't game, time, x all variables outside the scope of lambda y? What's the difference between x and these other variables/functions.
Thanks very much!
I am not sure why you are having the problem. In a minimal example like this:
(lambda x: (lambda y: y+x)(3))(2)
You get the expected 5, so there must be something more to what you are doing.
Related
This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Lambda in a loop [duplicate]
(4 answers)
Closed last year.
I am trying to generate a list of lambdas that I will later apply to an object, but when I try to do it via a comprehension or a loop over a list, the reference to the variable is kept, rather than the value itself. Let me illustrate.
Assume your object class is something like this:
class Object:
def function(self, x):
print(x)
So when you create the object and invoke it you get something like this:
o = Object()
o.function(0)
>>> 0
Now, if I manually construct my list of lambdas it would look like this:
lambdas = [
lambda x: x.function(0),
lambda x: x.function(1),
lambda x: x.function(2)
]
Which I can then apply to my previously created object:
for l in lambdas:
l(o)
>>> 0
>>> 1
>>> 2
However, when I generate the lambda list from another list, I only get the reference to the latest element of the list:
lambdas = [lambda x: x.function(i) for i in range(2)]
for l in lambdas:
l(o)
>>> 2
>>> 2
>>> 2
On closer inspection I can see that each lambda has a different memory address, so they are NOT references to the same function.
So I can only assume that the lambda is keeping a reference to i which has a final value of 2 and therefore when invoked, it takes the value.
So my question is if its possible to set the value of the variable inside the lambda before invocation?
Note: The usa case for a list of lambdas is to pass to the agg function of a Pandas groupby on a DataFrame. I am not looking for a solution to the pandas problem, but curious about the general solution.
Generator Option
Just change lambdas to a generator instead of a list, this will cause it redefine i on every call:
lambdas = (lambda x: x.function(i) for i in range(2))
for l in lambdas:
print(l(o))
Full code:
class Object:
def function(self, x):
print(x)
o = Object()
o.function(0) #manual call
lambdas = (lambda x: x.function(i) for i in range(2))
for l in lambdas:
l(o)
Output:
0 #output from manual call
0 #output from generator
1 #output from generator
List Option
If you need a list for things like lambdas[0](o) you can send i to lambda each iteration by using i=i like so:
lambdas = [lambda x, i=i: x.function(i) for i in range(2)]
Example of second option:
class Object:
def function(self, x):
print(x)
o = Object()
lambdas = [lambda x, i=i: x.function(i) for i in range(2)] #notice the cahnge
for i in range(len(lambdas)):
lambdas[i](o) #notice the change
Output:
0
1
What takes place is that in this expression, the "living" (nonlocal) i variable is
used inside each lambda created. And at the end of the for loop, its value is the last value taken - which will be used when the lambdas are actually called.
lambdas = [lambda x: x.function(i) for i in range(2)]
The fix for that is to create an intermediary namespace which will "freeze" the nonlocal variable value at the time the lambda is created. This is usually done with another lambda:
lambdas = [(lambda i: (lambda x: x.function(i)))(i) for i in range(2)]
So, bear with me - in the above expression, for each execution of the for i loop, a new, disposable lambda i is created and called imediatelly with the current value of the i used in the for. Inside it, this value is bound to a local i variable, that is unique to this disposable lambda i (in Python internal workings, it gets its own "cell"). This unique iis then used in the second, permanent, lambda x expression. Whenever that one is called, it will use the i value persisted in the outter lambda i call. The external lambda i then returns the lambda x expression as its result, but its nonlocal i is bound to the value used inside the lambda i, not the one used in the for i.
This is a common problem in Python, but can't be fixed because it is part of how the language works.
There is a shorter, and working, form to "freeze" the i from for i when each lambda i is created, that does not require an outer function scope: when a function is created, the values passed as default for its parameters are stored along with the function. Then, if one stores the current value of i as a default value, it won't change when the variable i itself does:
lambdas = [lambda x, i=i: x.function(i) for i in range(2)]
Here, in the lambda x, i=i: snippet, the value of i in the scope the lambda is created is stored as the default value for the parameter i, which works as a local (in contrast with a nonlocal) variable inside the lambda function itself.
I've tried to define a function that adds several lambda functions together.
When I call the function the kernel crashes and restarts with no further error messages.
def add_lambdas(function_list):
f = lambda x, y: None
for function in function_list:
f = lambda x, y: f(x, y) + function(x, y)
return f
f1 = lambda x, y: x + y
f2 = lambda x, y: x**2 - 2*y
f3 = lambda x, y: f1(x,y) + f2(x,y)
f4 = add_lambdas([f1,f2])
For example, when I call f3(2,3) all is well, but when I call f4(2,3) the kernel crashes and restarts. For f3 I do the adding of f1 and f2 manually, but for f4 I pass them through the function add_lambdas().
This is the output:
In[5]: f3(2,3)
Out[5]: 3
In[6]: f4(2,3)
Restarting kernel...
Obviously something is wrong with my defined function, but I cant figure out what it is. Any thoughts? Thank you!
I believe the problem is that you're defining f as a recursive function, which I don't think is what you want. You can try this instead:
def add_lambdas(function_list):
return lambda x, y: sum(f(x, y) for f in function_list)
You are getting a stack overflow. The problem is that there is only a single variable f and a single variable function in add_lambdas. When you write:
f = lambda x, y: f(x, y) + function(x, y)
you are just assigning a new value to the variable f, but the variable f inside the definition is also that new value. This is the stack overflow. In addition, the variable function will be left with the last value in the list. There is no reason for it to copy the value.
If instead you write:
f = lambda x, y, f=f, function=function: f(x, y) + function(x, y),
you're telling Python to assign f and function to new variables, (with the same name) and to use those new variables inside the lambda. Changes made to the other variables are immaterial
You'll discover a different error in your code. The first line needs to be
f = lambda x, y: 0 since otherwise you're adding an integer to None.
Note that #josemz is the simpler solution if you're just planning on adding things. If you want to learn to use lambdas within other lambdas, you need to make sure variables are bound correctly.
This question already has answers here:
Local variables in nested functions
(4 answers)
Closed 5 years ago.
I wanted to create a list of lambdas, but it didn't quite work out as I hoped.
L = [(lambda x: x/y) for y in range(10)]
I expected every function in the list to divide its argument by its index, but all functions only divide by the last index.
>>> L[1](5)
0.5555555555555556
>>> L[5](5)
0.5555555555555556
>>> 5/9
0.5555555555555556
Is this kind of list comprehension, where every lambda has its own copy of ypossible in Python?
The y in your lambda refers to the last value that y had in the scope it came from, i.e., 9.
The easiest way to get the behavior you want is to use a default argument in your lambda:
lambda x, y=y: x/y
This captures the value of y at the moment the lambda function is defined.
You can also do a "double-lambda", calling a function that returns the lambda you want, passing in the desired value of y:
(lambda y: lambda x: x/y)(y)
Here, the outer lambda provides a new scope each time you call it.
You need the loop and the lambda to be in different scopes.
def make_divider(y):
return lambda x: x / y
L = [make_divider(y) for y in range(10)]
print(L[2](5) == 5 / 2)
print(L[4](5) == 5 / 4)
This question already has answers here:
Local variables in nested functions
(4 answers)
Closed 5 years ago.
I wanted to create a list of lambdas, but it didn't quite work out as I hoped.
L = [(lambda x: x/y) for y in range(10)]
I expected every function in the list to divide its argument by its index, but all functions only divide by the last index.
>>> L[1](5)
0.5555555555555556
>>> L[5](5)
0.5555555555555556
>>> 5/9
0.5555555555555556
Is this kind of list comprehension, where every lambda has its own copy of ypossible in Python?
The y in your lambda refers to the last value that y had in the scope it came from, i.e., 9.
The easiest way to get the behavior you want is to use a default argument in your lambda:
lambda x, y=y: x/y
This captures the value of y at the moment the lambda function is defined.
You can also do a "double-lambda", calling a function that returns the lambda you want, passing in the desired value of y:
(lambda y: lambda x: x/y)(y)
Here, the outer lambda provides a new scope each time you call it.
You need the loop and the lambda to be in different scopes.
def make_divider(y):
return lambda x: x / y
L = [make_divider(y) for y in range(10)]
print(L[2](5) == 5 / 2)
print(L[4](5) == 5 / 4)
I'm trying to return from a function a list of functions, each of which uses variables from the outside scope. This isn't working. Here's an example which demonstrates what's happening:
a = []
for i in range(10):
a.append(lambda x: x+i)
a[1](1) # returns 10, where it seems it should return 2
Why is this happening, and how can I get around it in python 2.7 ?
The i refers to the same variable each time, so i is 9 in all of the lambdas because that's the value of i at the end of the loop. Simplest workaround involves a default argument:
lambda x, i=i: x+i
This binds the value of the loop's i to a local variable i at the lambda's definition time.
Another workaround is to define a lambda that defines another lambda, and call the first lambda:
(lambda i: lambda x: x+i)(i)
This behavior makes a little more sense if you consider this:
def outerfunc():
def innerfunc():
return x+i
a = []
for i in range(10):
a.append(innerfunc)
return a
Here, innerfunc is defined once, so it makes intuitive sense that you are only working with a single function object, and you would not expect the loop to create ten different closures. With a lambda it doesn't look like the function is defined only once, it looks like you're defining it fresh each time through the loop, but in fact it is functionally the same as the the long version.
Because i isn't getting evaluated when you define the anonymous function (lambda expression) but when it's called. You can see this by adding del i before a[1](1): you'll get NameError: global name 'i' is not defined on the a[1](1) line.
You need to fix the value of i into the lambda expression every time, like so:
a = [lambda x, i=i: x+i for i in range(10)]
a[1](1) # returns 2
Another, more general solution - also without lambdas:
import operator
from functools import partial
a = []
for i in range(10):
a.append(partial(operator.add, i))
a[1][(1) # returns 2
The key aspect here is functools.partial.