Simple Python Issue with Iteration and Lambda - python

lst = [1, 2, 3]
i = 0
f = lambda x: x * lst[i]
i = 1
print(f(10))
f = lambda x: x * lst[i]
i = 2
print(f(10))
f = lambda x: x * lst[i]
Above is my Python code and I thought it would print out 10, 20, but it says 20, 30. I don't understand why f is modified by i regardless of the explicit assignment. I've got to make it print 10, 20 using an iteration(actually the code is a simplified form of the original one), so it seems that f = lambda x: x * 1 is not allowed.

I think you're expecting f = lambda x: x * lst[i] to store value of i, but it doesn't work that way. When a function is defined, it is just stored to be used later, it is not evaluated when it is defined. It is evaluated only when it is called.
So, when you call f(10) for the first time, you're passing value of x as 10 and the interpreter looks up for the value of i in memory, which is 1 during first function call and 2 during second function call. That's why you get 20 30 as output.
Feel free to ask any question if you still have doubts.

i is global variable. so when you call f it uses the current value of i
look at
f = lambda x: x * lst[i]
lst = [1, 2, 3]
for i in range(len(lst)):
print(f(10))
output
10
20
30
Note, not related to your question, but f = lambda x: x * lst[i] is against PEP8 recommendations:
Always use a def statement instead of an assignment statement that
binds a lambda expression directly to an identifier:
Correct: def f(x): return 2*x
Wrong: f = lambda x: 2*x
The first form means that the name of the resulting function object is
specifically 'f' instead of the generic ''. This is more
useful for tracebacks and string representations in general. The use
of the assignment statement eliminates the sole benefit a lambda
expression can offer over an explicit def statement (i.e. that it can
be embedded inside a larger expression)

Imagine lambda expression as a truly function, so you will get these code:
lst = [1, 2, 3]
i = 0
def f(x):
return x * lst[i]
i = 1
print(f(10)) # currently x=10 and i=1
def f(x):
return x * lst[i]
i = 2
print(f(10)) # currently x=10 and i=2
def f(x):
return x * lst[i]
I know what you mean. In your head, while you define lambda x: x * lst[i], you think number i will be frozen at this time --- just like assignment. When it be called later, the number i will always be the first value. It's not True. The value of i will be referenced when you call this function.

Related

Reframe pipeline function with a generator

I have a pipeline function that takes an arbitrary number of functions as arguments, it returns a single function helper which contain one argument and this function in turn calls the pipeline function with a single argument iteratively. Here is the code:
def pipeline(*funcs):
def helper(arg):
for func in funcs:
result = func(arg)
arg = result
return result
return helper
And here a test case:
fun = pipeline(lambda x: x * 3, lambda x: x + 1, lambda x: x / 2)
print(fun(3)) #should print 5.0
I was reading on a different question about generators and how they can be used to remember previous state here, and was wondering if I could reframe my current pipeline function to use a generator instead. I need to remember the arg for every func call and currently I'm doing that by storing into a variable, however since generators can remember the last state I was wondering if I could use it instead.
You could (but absolutely shouldn't) create a side-effect based list-comprehension that uses the walrus-operator:
funcs = [lambda x: x * 3, lambda x: x + 1, lambda x: x / 2]
x = 3
[x := f(x) for f in funcs]
print(x)
What makes this even worse than it already is is the fact that if you use a simple generator instead of the list-comprehension the result is different because it doesn't all get executed before the print.
Or you even force it all in one line like this:
print([x := f(x if i>0 else 3) for i, f in enumerate(funcs)][-1])
I think generally a prettier approach would be to just wrap it into a normal for loop:
x = 3
for f in funcs:
x = f(x)
print(x)

Python Multiple Function Composition

So i have a homework question, but I'm not sure why I got it wrong / how it works.
once = lambda f: lambda x: f(x)
twice = lambda f: lambda x: f(f(x))
thrice = lambda f: lambda x: f(f(f(x)))
print(thrice(twice)(once)(lambda x: x + 2)(9))
My ans: 25 -> 8*2 +9
Actual ans: 11 -> 2 + 9
What I was thinking:
thrice -> f(f(f(x))),
let new_x = twice(x)
thrice -> f(f(new_x)),
let new_x2 = twice(new_x)
thrice -> f(new_x2),
let new_thrice = twice(new_x2)
so afterwards I add in the (once) and did
new_thrice(once)(lambda x: x+2)(9)
But answer seems to be that (once) nullifies the earlier thrice(twice) and am lost about. Would be great if someone has an explanation.. Thanks!
I hope this will help you to figure out what is going on!
once = lambda f: lambda x: f(x)
twice = lambda f: lambda x: f(f(x))
thrice = lambda f: lambda x: f(f(f(x)))
# Created this one to help readability.
custom_func = lambda x: x + 2
print("once:", once(custom_func)(9)) # returns value
print("twice:", twice(custom_func)(9)) # returns value
print("thrice:", thrice(custom_func)(9)) # returns value
print("applying all those:", thrice(custom_func)(twice(custom_func)(once(custom_func)(9))))
# This represents the following: (((9 + 2) + 2 + 2) + 2 + 2 + 2)
# each pair of parenthesis mean one function being applied, first once, then twice, then thrice.
# If I've understood correctly you need to achieve 25
# to achieve 25 we need to apply +4 in this result so, which means +2 +2, twice function...
print("Achieving 25:", twice(custom_func)(thrice(custom_func)(twice(custom_func)(once(custom_func)(9)))))
# That is it! Hope it helps.
once(lambda x: x+2) evaluates to a function that applies lambda x: x+2 to its argument. In other words, it's equivalent to lambda x: x+2.
once(once(lambda x: x+2)) evaluates to a function that applies once(lambda x: x+2) to its argument. In other words, it's also equivalent to lambda x: x+2.
once(once(once(lambda x: x+2))) evaluates to a function that applies once(once(lambda x: x+2)) to its argument. In other words, this is also equivalent to lambda x: x+2. This doesn't change no matter how many times you apply once.
thrice(twice)(once) evaluates to a function that applies once to its argument some number of times. (8 times, not that it matters for the analysis.) once doesn't change a function's behavior. No matter how many times you apply once, the final function only applies the underlying function once.
thrice(twice)(once)(lambda x: x + 2) thus evaluates to a function that does the same thing as lambda x: x + 2.
Now, if it had been thrice(twice)(once(lambda x: x + 2)) (note the moved parentheses), then that would have applied thrice(twice) to once(lambda x: x + 2), and the result would be a function that applies lambda x: x + 2 8 times.

python filter doesn't work

I have an algorithm that can generate a prime list as a generator:
def _odd_iter():
n=3
while True:
yield n
n=n+2
def _not_divisible(n):
return lambda x: x % n > 0
def primes():
yield 2
L=_odd_iter()
while True:
n=next(L)
yield n
L=filter(_not_divisible(n), L)
x=1
for t in primes():
print(t)
x=x+1
if x==10:
break
But if I put the lambda function into the filter function directly, like below:
def primes():
yield 2
L=_odd_iter()
while True:
n=next(L)
yield n
L=filter(lambda x: x%n>0, L)
I can get only an odd list, not a prime list. It seems the filter function doesn't work.
What can I do?
Here's a simpler program which illustrates the same problem.
adders = []
for i in range(4):
adders.append(lambda a: i + a)
print(adders[0](3))
While one would expect the output to be 3, the actual output is 6.
This is because a closure in python remembers the name and scope of a variable rather than it's value when the lambda was created. Since, i has been modified by the time the lambda is used, the lambda uses the latest value of i.
The same thing happens in your function. Whenever n is modified, all the lambda functions in the various filters also get modified. So, by the time the iterator reaches 9, all the filters are filtering factors of 7, not 5 or 3.
Since, in your first approach you are creating a new scope with each call to _not_divisible, the function works as intended.
If you absolutely must use a lambda directly, you can use a second argument like this:
def primes():
yield 2
L=_odd_iter()
while True:
n=next(L)
yield n
L=filter(lambda x, n=n: x%n>0, L)
The lambda that works is lambda x, n=n: x%n != 0. You apparently need to do this if you want n to be captured at the time the lambda is defined. Otherwise a lambda only looks up the variable name when it gets around to evaluating the lambda. In your case I think that meant locking onto an n value in a later iteration of the while loop.

Python Generator "chain" in a for loop

I'm trying to set up a "processing pipeline" for data that I'm reading in from a data source, and applying a sequence of operators (using generators) to each item as it is read.
Some sample code that demonstrates the same issue.
def reader():
yield 1
yield 2
yield 3
def add_1(val):
return val + 1
def add_5(val):
return val + 5
def add_10(val):
return val + 10
operators = [add_1, add_5, add_10]
def main():
vals = reader()
for op in operators:
vals = (op(val) for val in vals)
return vals
print(list(main()))
Desired : [17, 18, 19]
Actual: [31, 32, 33]
Python seems to not be saving the value of op each time through the for loop, so it instead applies the third function each time. Is there a way to "bind" the actual operator function to the generator expression each time through the for loop?
I could get around this trivially by changing the generator expression in the for loop to a list comprehension, but since the actual data is much larger, I don't want to be storing it all in memory at any one point.
You can force a variable to be bound by creating the generator in a new function. eg.
def map_operator(operator, iterable):
# closure value of operator is now separate for each generator created
return (operator(item) for item in iterable)
def main():
vals = reader()
for op in operators:
vals = map_operator(op, vals)
return vals
However, map_operator is pretty much identical to the map builtin (in python 3.x). So just use that instead.
You can define a little helper which composes the functions but in reverse order:
import functools
def compose(*fns):
return functools.reduce(lambda f, g: lambda x: g(f(x)), fns)
I.e. you can use compose(f,g,h) to generate a lambda expression equivalent to lambda x: h(g(f(x))). This order is uncommon, but ensures that your functions are applied left-to-right (which is probably what you expect):
Using this, your main becomes just
def main():
vals = reader()
f = compose(add_1, add_5, add_10)
return (f(v) for v in vals)
This may be what you want - create a composite function:
import functools
def compose(functions):
return functools.reduce(lambda f, g: lambda x: g(f(x)), functions, lambda x: x)
def reader():
yield 1
yield 2
yield 3
def add_1(val):
return val + 1
def add_5(val):
return val + 5
def add_10(val):
return val + 10
operators = [add_1, add_5, add_10]
def main():
vals = map(compose(operators), reader())
return vals
print(list(main()))
The reason for this problem is that you are creating a deeply nested generator of generators and evaluate the whole thing after the loop, when op has been bound to the last element in the list -- similar to the quite common "lambda in a loop" problem.
In a sense, your code is roughly equivalent to this:
for op in operators:
pass
print(list((op(val) for val in (op(val) for val in (op(val) for val in (x for x in [1, 2, 3])))))
One (not very pretty) way to fix this would be to zip the values with another generator, repeating the same operation:
def add(n):
def add_n(val):
return val + n
return add_n
operators = [add(n) for n in [1, 5, 10]]
import itertools
def main():
vals = (x for x in [1, 2, 3])
for op in operators:
vals = (op(val) for (val, op) in zip(vals, itertools.repeat(op)))
return vals
print(list(main()))

Python: Lambda in a sum

I'm trying to do the following, which is a representative example of what my final goal will be:
yu = lambda x: 0
for i in range(0,5):
yu = lambda x: i + yu(x)
Unfortunately, it returns:
RuntimeError: maximum recursion depth exceeded
when I do:
print yu(0)
The print statement should return 10.
What's the correct way to do this?
In the end, you have:
yu = lambda x: i + yu(x)
but yu will be looked up at runtime, not when you constructed the lambda. Do this instead:
for i in range(0,5):
yu = lambda x, yu=yu: i + yu(x)
This does not return 10, though. It returns 20 instead:
>>> yu = lambda x: 0
>>> for i in range(0,5):
... yu = lambda x, yu=yu: i + yu(x)
...
>>> yu(0)
20
because now i is still looked up from the context (and by now the loop has finished so it's 4). Solution? Move i to a keyword argument too:
for i in range(0,5):
yu = lambda x, yu=yu, i=i: i + yu(x)
Now this works:
>>> yu = lambda x: 0
>>> for i in range(0,5):
... yu = lambda x, yu=yu, i=i: i + yu(x)
...
>>> yu(0)
10
Moral of the story? Bind your context properly to the scope of the lambda.
yu = lambda x: i + yu(x)
This makes yu into a function that always calls itself, guaranteeing infinite recursion with no base case.
Why? Well, you've built a closure where yu (and i) are the local variables in the function or module that the for loop is part of. That's not what you want; you want to close over the current values of yu and i, not the outer variables.
I'm not sure why you're even using lambda in the first place. If you want to define a function and give it a name, use def.
Here's an easy solution:
def yu(x): return 0
def make_new_yu(yu, i):
def new_yu(x): return i + yu(x)
return new_yu
for i in range(0, 5):
yu = make_new_yu(yu, i)
By making the wrapping explicit, the correct way to do it becomes the most obvious way to do it.
You can, of course, use a lambda inside make_new_yu without making things more confusing:
def make_new_yu(yu, i):
return lambda x: i + yu(x)
And you can even make the initial definition a lambda if you want. But if you insist on not having any def statements, you need to force the right values into the closure in some way, e.g., by using the default-value trick. That's much easier to get wrong—and harder to read once you've done it.
If you want an intuitive understanding of the difference, without learning the details: a function body defines a new scope. So, defining the function (by lambda or def) inside that function means your closure is from that new scope.
I believe I didn't fully understand your question, but could anyone check if this is what he meant?
I assumed you wouldn't need an iterator to generate a list of digits
yu = lambda x: x[0] + yu(x[1:]) if x!=[] else 0
facto = lambda f: f if f == 0 else f + facto(f-1)
print(facto(4))
Considering the answers you try to sum up 0, 1, 2, 3, 4. If that was right you could use the following lambda expression:
yu = lambda x: x + yu(x+1) if x<4 else x
For yu(0) it delivers 10 as a result. The break condition is the value of x which is required to stay smaller than 4 in order to add up.
Assuming that is what you desired to do, you should leave out the loop for a rather concise statement.
This lambda expression differentiates from the others in resulting in different values (other than 10, when not choosing 0 as the parameter x) depending on the given argument.

Categories

Resources