This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 6 months ago.
I have seen this question, but i still cannot get why such simple example does not work:
mylist = ["alice", "bob", "greta"]
funcdict = dict(((y, lambda x: x==str(y)) for y in mylist))
funcdict['alice']("greta")
#True
funcdict['alice']("alice")
#False
funcdict['greta']("greta")
#True
How is it different from:
[(y, y) for y in mylist]
Why y is not evalueated within each step of iteration?
The y in the body of the lambda expression is just a name, unrelated to the y you use to iterate over mylist. As a free variable, a value of y is not found until you actually call the function, at which time it uses whatever value for y is in the calling scope.
To actually force y to have a value at definition time, you need to make it local to the body via an argument:
dict((y, lambda x, y=z: x == str(y)) for z in mylist)
((y, lambda x: x==str(y)) for y in mylist)
y inside the lambda is not bound at the time of the genration expression defined, but it's bound when it's called; When it's called, iteration is already done, so y references the last item greta.
One way to work around this is to use keyword argument, which is evaluated when the function/lambda is defined:
funcdict = dict((y, lambda x, y=y: x == y) for y in mylist)
funcdict = {y: lambda x, y=y: x == y for y in mylist} # dict-comprehension
or you can use partial:
funcdict = {y: partial(operator.eq, y) for y in mylist}
y is evaluated while the mylist is iterated.
Related
I am trying to generate a scatter plot with x in range (0,17) and y = 1/(x**2+1). Here's the code that I use to generate the lambda function for y:
y = [lambda x:1/(x**2+1) for x in range(17)]
y
Apparently it shows this output 17 times:
<function __main__.<listcomp>.<lambda>(x)>,
<function __main__.<listcomp>.<lambda>(x)>,
<function __main__.<listcomp>.<lambda>(x)>,
<function __main__.<listcomp>.<lambda>(x)>,
What did I do wrong with the code above? Thanks!
You're not calling the function in the loop.
You can either do
f = lambda x: 1 / (x ** 2 + 1)
y = [f(x) for x in range(17)]
or forgo the lambda and just
y = [1 / (x ** 2 + 1) for x in range(17)]
If you indeed wanted to use lambda inside list comp, you should have done this:
>>> y = [(lambda x:1/(x**2+1))(x) for x in range(17)]
>>> y
[1.0, 0.5, 0.2, 0.1, 0.058823529411764705, 0.038461538461538464, 0.02702702702702703, 0.02, 0.015384615384615385, 0.012195121951219513, 0.009900990099009901, 0.00819672131147541, 0.006896551724137931, 0.0058823529411764705, 0.005076142131979695, 0.004424778761061947, 0.0038910505836575876]
Here the x with the lambda has nothing to do with the x being iterated, it is the parameter of the lambda function. The x in the function call outside the parentheses that encloses lambda is the value you are passing to the function.
This was just for explanation purposes, it does not make any sense to write programs this way, the accepted answer is the way you should go.
However, if you want to go crazy with lambda, you can watch David Baezley's talk on lambda calculus with python.
I want to generate an array in python numpy based on equation; 1/x for x = 1,2,3,...10 and I wrote; But I wasn't getting any output, please help
def Number(x):
for x in range (1,11):
y = 1/x
return y
y = Number(10)
print y
If you want to return an list, you need to actually make a list and append to it (or use a list comprehension). You just create a variable and assign to it. Instead do:
def Number(x):
y = []
for x in range (1,11):
y.append(1./x)
return y
y = Number(10)
print y
Now if you want to, you could use a list comprehension. This is a pythonic way to generate a list in a single line. It would look something like this.
def Number(x):
y = [1./x for x in range(1,11)]
return y
y = Number(10)
print y
Another way to do this is to use the map builtin, which is different in python 3, but you are using 2.7, so we are good.
def Number(x):
y = map(lambda x: 1./x, range(1,11))
return y
y = Number(10)
print y
The map function is applies the function to the specified list. In this case, I use an anonymous lambda function lambda x:1./x which is a simple way of writing a function with x as an argument and returns 1/x. The map function applies the lambda function to each element in the list.
This can be done as follow
def Number(n):
return np.array([1/i for i in range(1, n)])
now you can do that
y = Number(10)
I've just stumbled about some unexpected behavior in Python in the below code snippet
b = False
func_1 = lambda x,y:set([x]) == y if b else lambda x,y: x in y
func_2 = None
if not b:
func_2 = lambda x,y : x in y
else:
func_2 = lambda x,y:set([x]) == y
print(func_1("Hello", set(["Hello", "World"])))
print(func_2("Hello", set(["Hello", "World"])))
The output is
<function <lambda>.<locals>.<lambda> at 0x7f7e5eeed048>
True
However, when adding brackets around the lambdas everything works as expected:
func_1 = (lambda x,y:set([x]) == y) if b else (lambda x,y: x in y)
# ...
The output then is
True
True
Why do I need those brackets? I thought the initial expression was equivalent to the long if-else construct.
It's just standard precedence rules. Your first expression is being parsed as:
lambda x,y:set([x]) == (y if b else lambda x,y: x in y)
So you need to add the parentheses to create the correct precedence.
I tried the following code in Python 3.5.1:
>>> f = {x: (lambda y: x) for x in range(10)}
>>> f[5](3)
9
It's obvious that this should return 5. I don't understand where the other value comes from, and I wasn't able to find anything.
It seems like it's something related to reference - it always returns the answer of f[9], which is the last function assigned.
What's the error here, and how should this be done so that it works properly?
Python scoping is lexical. A closure will refer to the name and scope of the variable, not the actual object/value of the variable.
What happens is that each lambda is capturing the variable x not the value of x.
At the end of the loop the variable x is bound to 9, therefore every lambda will refer to this x whose value is 9.
Why #ChrisP's answer works:
make_func forces the value of x to be evaluated (as it is passed
into a function). Thus, the lambda is made with value of x currently
and we avoid the above scoping issue.
def make_func(x):
return lambda y: x
f = {x: make_func(x) for x in range(10)}
The following should work:
def make_func(x):
return lambda y: x
f = {x: make_func(x) for x in range(10)}
The x in your code ends up referring to the last x value, which is 9, but in mine it refers to the x in the function scope.
What is the Python mechanism that makes it so that
[lambda: x for x in range(5)][2]()
is 4?
What is the usual trick for binding a copy of x to each lamba expression so that the above expression will equal 2?
My final solution:
for template, model in zip(model_templates, model_classes):
def create_known_parameters(known_parms):
return lambda self: [getattr(self, p.name)
for p in known_parms]
model.known_parameters = create_known_parameters(template.known_parms)
>>> [lambda x=x: x for x in range(5)][2]()
2
I usually use functools.partial:
[ partial(lambda x: x, x) for x in range(5) ]
Or, of course, you can do it yourself:
[ (lambda x: (lambda: x))(x) for x in range(5) ]
Since no one's answered the "what is the mechanism" part, and this surprised me when I first read it, here's a go:
This:
ls = [lambda: x for x in range(5)]
Is a bit like this:
ls = []
x = 0
ls.append(lambda: x)
x = 1
ls.append(lambda: x)
x = 2
ls.append(lambda: x)
x = 3
ls.append(lambda: x)
x = 4
ls.append(lambda: x)
Each of those lambdas has its own scope, but none of those scopes contain an x. So they're all going to be reading the value of x by looking in an outer scope, so of course they must all be referring to the same object. By the time any of them are called, the loop is done and that object is the integer 4.
So even though these lambdas look like functions involving only immutable values, they can still be affected by side effects, because they depend on the bindings in an outer scope, and that scope can change.
You could of course further change what these lambdas return by rebinding x, or make them throw an error by unbinding it. In the list comprehension version though, the x is only bound in a private scope inside the list comprehension, so you can't mess with it (as easily).
The solution is of course to arrange things so that each lambda has an x in its local scope (or at least some outer scope that is not shared between the lambdas), so that they can all refer to different objects. Ways to do that have been shown in the other answers.