This question already has answers here:
Local variables in nested functions
(4 answers)
Closed 4 years ago.
I am in need of creating functions (a lot) and storing inside an array or list. I tried using list comprehensions for generating functions (like the code shown below, indeed a simple case).
f = [lambda x: x*i for i in range(0,3)]
which gives
f[0](1) = 2 # expected 0
f[1](1) = 2 # expected 1
f[2](1) = 2 # expected 2 (no prblm with last case)
But instead of using iterators (like list comprehensions) to generate functions, if I write functions explicitly inside a list(defining each function), it works fine.
So, is there anything wrong with the above code(which uses iterators to generate anonymous functions)?
I believe this variant of the syntax will give you what you want:
f = [lambda x, i=n: x * i for n in range(0, 3)]
EXAMPLES
>>> f[0](1)
0
>>> f[1](1)
1
>>> f[2](1)
2
>>>
I believe what #user2357112 is leading you towards is the variation:
from functools import partial
f = [partial(lambda x, i: x * i, i=n) for n in range(0, 3)]
which may not be easy to tease out of the cited duplication page.
The inside of your lambda function is evaluated when you call the function, not when you create the function. At this point point the value of i is 2. You can verify this by running:
>>> f = [lambda x: x*i for i in range(0,3)]
>>> i
2
As cdlane pointed out in his answer, you can fix this by storing the values from the range() statement into a default parameter on your lambda function:
>>> f = [lambda x, i=i: x*i for i in range(0,3)]
>>> f[0](1)
0
>>> f[1](1)
1
>>> f[2](1)
2
This works because unlike the function body, the function definition is evaluated when you create the function.
Related
This question already has answers here:
What do lambda function closures capture?
(7 answers)
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 6 years ago.
I have a back-ground of C++ and trying to learn some python.
Whilst i understand virtual functions for C++, I unfortunately do not understand what is meant by late binding of closures in python.
Link: https://gist.github.com/deemson/8efabf56d67623ead804 (no longer works)
Copy-pasted from a tutorial:
functions = []
for n in [1, 2, 3]:
def func(x):
return n*x
functions.append(func)
# You would expect this to print [2, 4, 6]
print(
'calling a list of bad closures and output is: {}'
.format(str([function(2) for function in functions]))
)
What exactly is happening here? When the function is appended in to the list, what values does it have? Can someone please simplify this code for me to understand?
Notice this, you can create functions on runtime, more or less like lambdas in c++. So basically you are iterating over a list, making n take values 1,2 and 3
for n in [1, 2, 3]:
def func(x):
return n*x
so, by each iteration you are building a function named func, with takes a value and multiplies it for n. By appending it to the functions list you will have this functions stored, so you can iterate over the list to call the functions.
[function(2) for function in functions]
By doing this you call each of the functions stored with the value 2, you would expect this to output [2, 4, 6] ([1*2, 2*2, 3*2]), but instead it returns [6, 6, 6], WHY?, thats because every function use n for its computation, so they are not really doing 1*x, 2*x and 3*x but actually n*x and since n is bonded in last time to 3 all functions are doing 3*2 which becomes 6.
Play around with the python console to check it properly.
In the language of C++, a pointer to the function is what's being appended to the list. After the for loop, functions contains pointers to three different functions (func(x) = n * x, func(x) = n * x and func(x) = n * x). Note the dependency on n. As n changes, so will the behavior of these functions, and they are all equivalent.
In the second part of the code, the pointers are extracted from the list and each of the three functions is evaluated with an argument of 2.
Here's a further example to clarify. Imagine we do this:
>>> functions
[<function func at 0x0239AA70>, <function func at 0x0239AAB0>, <function func at 0x0239AB30>]
>>> g = functions[2]
>>> g
<function func at 0x0239AB30>
>>> g(10)
20
>>> g(100)
200
What we're seeing in that first lines is that functions contains pointers to three different functions. The next line extracts the third pointer from the list (which refers to func(x) = n * x) and assigns it to g. Effectively, we have defined a function g(x) = n * x with this call. We can now evaluate g with arguments.
Note that since all functions depend on n, you could change n, and the behavior would change.
>>> n = 100
>>> g(10)
1000
This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
What do lambda function closures capture?
(7 answers)
Closed 6 months ago.
This is so weird, if I make a list of lambda functions and with a for, I print the output of each function, everything works, but if I individually print the output of a single function like the first one, it gives me the output of the last function or I don't even know what it gives me, for example:
X = [1,2,3,4]
L = []
for i in range(len(X)):
L.append(lambda x: X[i]**x)
for i in range(len(X)):
print(L[i](2))
This gives me:
1
4
9
16
That is correct, but if i want only the first one:
print(L[0](2))
# -> 16
And if I want the second one does the same, and so on, I checked the lambda functions were all different and they are. I don't know what's going on
The lambda references the global variable i, so after the for loop, i==3, computing X[3]**2:
X = [1,2,3,4]
L = []
for i in range(len(X)):
L.append(lambda x: X[i]**x)
for f in L:
print(f(2))
Output:
16
16
16
16
A way to fix is to capture the current value of global i as a local parameter i when the function is defined:
X = [1,2,3,4]
L = []
for i in range(len(X)):
L.append(lambda x, i=i: X[i]**x) # capture i as a parameter
for f in L:
print(f(2))
Output:
1
4
9
16
You are expecting the value of i to be part of the function, not the name. That's not how function definitions work, either with def statements or lambda expressions.
I'd recommend defining a function maker, so that your expected function can close over the local value of i.
def make_function(i):
return lambda x: X[i]*x*x
Now i refers not to a global variable i, but to a local variable i in the function call. Every call to make_function creates a different local variable initialized with the value of the argument, and that variable is what your function will refer to when it is called.
for i in range(len(X)):
L.append(make_function(i))
Your lambda closes around the variable i. Despite what the for loops make it look like, there's actually only one variable i in your entire code, so when you call L[i](2) or L[0](2), you're using the current value of i. Let's look at your first example.
for i in range(len(X)):
print(L[i](2))
Here, we call L[i] with the enclosing value of i, so this is really basically
for i in range(len(X)):
print(X[i] ** 2)
On the other hand, in your second example
print(L[0](2))
The i value is the i from the final loop iteration above. To get around this, you need to explicitly capture the current value of i.
L.append(lambda x, i=i: X[i]**x)
This exploits one of Python's least intuitive features to explicitly capture a value in a lambda.
This question already has answers here:
What do lambda function closures capture?
(7 answers)
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 6 months ago.
I am trying to make a program that creates a list of lambda functions of the format y=mx+b, where 'm' and 'b' are predetermined values
My overall goal is to implement a function that
Takes a picture
Finds the lines on it
Extends them across the whole picture in a solid colour
Basically, something like a Hough transforms if you know what that is.
Once I have the lines for a specific image, I can create a lambda function to represent the slope of the line and where it begins. I'm having an issue not being able to append a lambda function to the list.
I have tried this :
if __name__ == "__main__":
nums = []
for i in range(10):
j = lambda x: x + i
nums.append(j)
for i in nums:
print(i(1))
Here is the error I'm getting :
Traceback (most recent call last):
File "C:/Users/me/.PyCharmCE2018.3/config/scratches/scratch_3.py", line 7, in <module>
print(i(1))
File "C:/Users/me/.PyCharmCE2018.3/config/scratches/scratch_3.py", line 4, in <lambda>
j = (lambda x: x + i)
TypeError: unsupported operand type(s) for +: 'int' and 'function'
The problem is that the lambdas you create are referring to the current value of i in the active stack frame. When you later reuse i for the second for loop, it is bound to the lambdas in your list. When invoked as i(1), the lambdas are trying to evaluate 1 + i where i is the lambda, so of course you get an error.
Probably what you want is to freeze the value of i at the point at which the lambda is created. You can do this by replacing:
j = lambda x: x + i
with:
j = (lambda y: lambda x: x + y)(i)
This effectively captures the current value of i by binding it to a lambda variable, then immediately applying that lambda, after which the binding remains fixed.
This will give you a clue:
>>> i=1
>>> a=lambda x:x+i
>>> a(5)
6
>>> i=2
>>> a(5)
7
lambda uses i in the outer scope. In the OP case, all the functions are the same. Using i in the final loop makes i a function, not an integer. Change it to something else, and you'll find the functions are all the same, using the last value of i:
nums = []
for i in range(10):
j = lambda x: x + i
nums.append(j)
for f in nums:
print(f(1))
10
10
10
10
10
10
10
10
10
10
The fix is, make i a parameter to the function, to capture the value as a local variable:
nums = []
for i in range(10):
j = lambda x,i=i: x + i
nums.append(j)
for f in nums:
print(f(1))
1
2
3
4
5
6
7
8
9
10
Your value of i has changed and it's not what you think.
First you create lambda:
j = lambda x: x + i
in hope, that i will remain as CURRENT value (so 0, 1, 2 and so on).
Then you execute it:
print(i(1))
Do you see, how you named your second iterator variable i? Change it to j and your example will work. Why? Because python resolves value of i in your lambda, when you execute it, not when you define it. So when you execute your lambda (i(1)) it will go to your lambda body and try x + i. Then it will lookup i, which now contains your lambda (not the INTEGER!). Hence your problem.
You need to do double function to make it work properly. Try this:
if __name__ == "__main__":
nums = []
for i in range(10):
def generate_lambda(i):
return lambda x: x + i
j = generate_lambda(i)
nums.append(j)
for i in nums:
print(i(1))
Why does this work? When you call generate_lambda, there will be i variable with your INTEGER value. It will shadow variable i used later on to iterate over lambdas. And since you never modify i variable inside generate_lambda function, it will stay like this forever.
I think you need to learn something more about lambda functions...
Actually, it's syntax is like : [lambda arguments: expression]
So, the issue is you have two variables in the expression, so you need to pass two arguments.
I don't really get what you want to achieve by this function, but I guess you need to have two arguments for m and b.
In your code, you need to initialize x and pass it as an argument to lambda.
nums = []
x=0
for i in range(10):
j = lambda x,i : x + i
nums.append(j)
for i in nums:
print(i(1,1))
You can use operator.add and functools.partial and do not lambda at all:
import operator
import functools
if __name__ == "__main__":
nums = []
for i in range(10):
nums.append(functools.partial(operator.add, i))
for i in nums:
print(i(1))
I can't do most parts of this question without peeking at the answer key, so I figured I could ask it here. Frankly, I've always had a problem understanding functions with no parameters, and whenever I see one, I think, "OH! they must mean "print" function!" When I'm actually overcomplicating things.
As for Exercise 5, I'm not understanding how to even evaluate the expressions. I feel like it's a simple question, but I can't compute an answer to any of them.
Exercise 4: A Fistful of Functions
For each of the following expressions, what must f be in order for the evaluation of the expression to succeed, without causing an error? Give a definition of f for each expression such that evaluating the expression will not cause an error.
f
f()
f(3)
f()()
f()(3)()
Solution
f = 3
f = lambda: 3
f = lambda x: x
f = lambda: lambda: 3
f = lambda: lambda x: lambda: x
Exercise 5: For A Few Lambdas More
Find the value of the following three expressions, using the given values of t and s.
t = lambda f: lambda x: f(f(f(x)))
s = lambda x: x + 1
t(s)(0) # 1
t(t(s))(0) # 2
t(t)(s)(0) # 3
Solution
1.) 3
2.) 9
3.) 27
For Exercise 4, you are given the following questions and answers.
Q: f
A: f = 3
This is essentially variable assignment.
Q: f()
A: f = lambda: 3
This is similar to a function that takes in no arguments and returns the value 3.
Q: f(3)
A: f = lambda x: x
This is similar to a function that takes in the argument x and returns the value x. So you are given the value 3 and a value of 3 is returned.
Q: f()()
A: f = lambda: lambda: 3
This is similar to a function that takes in no arguments and returns another function that again takes no arguments which will return the value 3.
Q: f()(3)()
A: f = lambda: lambda x: lambda: x
Like the previous example, this will return 3. You have function that takes in no arguments but returns another function that takes in the value x and returns another function that takes in no arguments but returns the value x.
By following the examples above, you can solve Exercise 5 in a similar fashion.
Additionally, Exercise 5 has been explained pretty well in the comments by ChrisP.
So today in computer science I asked about using a function as a variable. For example, I can create a function, such as returnMe(i) and make an array that will be used to call it. Like h = [help,returnMe] and then I can say h1 and it would call returnMe("Bob"). Sorry I was a little excited about this. My question is is there a way of calling like h.append(def function) and define a function that only exists in the array?
EDIT:
Here Is some code that I wrote with this!
So I just finished an awesome FizzBuzz with this solution thank you so much again! Here's that code as an example:
funct = []
s = ""
def newFunct(str, num):
return (lambda x: str if(x%num==0) else "")
funct.append(newFunct("Fizz",3))
funct.append(newFunct("Buzz",5))
for x in range(1,101):
for oper in funct:
s += oper(x)
s += ":"+str(x)+"\n"
print s
You can create anonymous functions using the lambda keyword.
def func(x,keyword='bar'):
return (x,keyword)
is roughly equivalent to:
func = lambda x,keyword='bar':(x,keyword)
So, if you want to create a list with functions in it:
my_list = [lambda x:x**2,lambda x:x**3]
print my_list[0](2) #4
print my_list[1](2) #8
Not really in Python. As mgilson shows, you can do this with trivial functions, but they can only contain expressions, not statements, so are very limited (you can't assign to a variable, for example).
This is of course supported in other languages: in Javascript, for example, creating substantial anonymous functions and passing them around is a very idiomatic thing to do.
You can create the functions in the original scope, assign them to the array and then delete them from their original scope. Thus, you can indeed call them from the array but not as a local variable. I am not sure if this meets your requirements.
#! /usr/bin/python3.2
def a (x): print (x * 2)
def b (x): print (x ** 2)
l = [a, b]
del a
del b
l [0] (3) #works
l [1] (3) #works
a (3) #fails epicly
You can create a list of lambda functions to increment by every number from 0 to 9 like so:
increment = [(lambda arg: (lambda x: arg + x))(i) for i in range(10)]
increment[0](1) #returns 1
increment[9](10) #returns 19
Side Note:
I think it's also important to note that this (function pointers not lambdas) is somewhat like how python holds methods in most classes, except instead of a list, it's a dictionary with function names pointing to the functions. In many but not all cases instance.func(args) is equivalent to instance.__dict__['func'](args) or type(class).__dict__['func'](args)