Lambda function range() get one number always - python

def multipliers():
return [lambda x: i * x for i in range(3)]
print([m(2) for m in multipliers()])
how to fix this lambda function?
I except:
[0, 2, 4]
I got:
[4, 4, 4]

This has to do with how the lambdas capture the name i, not its value (and the last value of the name i within that listcomp will be 2).
Add one more function that will have a local name i:
def make_multiplier(x):
return lambda y: x * y
def multipliers():
return [make_multiplier(i) for i in range(3)]
print([m(2) for m in multipliers()])

lambda is also a function with its local scope. To tie each lambda to a respective i counter you can pass it as an argument with default value:
def multipliers():
return [lambda x, i=i: i * x for i in range(3)]
print([m(2) for m in multipliers()]) # [0, 2, 4]

Related

How to solve lambda() takes 1 positional argument but 2 were given

I have the following code snippet but it is giving me the type error above. What I basically wanted to do is add two to every element and reduct it to a single sum, but it didnt work. How can we achieve this??
import functools
list1 = [1,2,3,4]
result = functools.reduce(lambda x:x+2, list1)
print(result)
Update:
It works when I do the following though:
list1 = [1,2,3,4]
result = functools.reduce(lambda x,y:x+y, list1)
print(result)
reduce() requires a lambda function which takes 2 arguments.
The first argument is the collector.
But here, so that +2 is applied also on the first argument, you have to give the initial value 0.
import functools
list1 = [1,2,3,4]
result = functools.reduce(lambda acc, el: acc + el + 2, list1, 0)
print(result) ## sum([3, 4, 5, 6]) = 18
You basically want to do a normal summing reduce(lambda x, y: x + y, lst) => sum(lst)
and a map(lambda x: x + 2, l) which is expressed in a more pythonic way as a list comprehension:
l = [1, 2, 3, 4]
sum([x + 2 for x in l]) # most pythonic!
## 18
sum(map(lambda x: x + 2, l))
## 18
or:
import functools
l = [1, 2, 3 ,4]
result = functools.reduce(lambda x, y: x + y, [x + 2 for x in l])
result ## 18
or:
import functools
l = [1, 2, 3 ,4]
result = functools.reduce(lambda x, y: x + y, map(lambda x: x + 2, l))
result ## 18
The callable passed to the reduce function needs to take two arguments to aggregate the current result with the next item from the given iterable:
import functools
list1 = [1, 2, 3, 4]
result = functools.reduce(lambda x, y: x + y + 2, list1, 0)
print(result)
This outputs:
18

Change instance of y to z in x(where x is a list)

I wrote a function that changes all instances of y to z in x (where x is a list) but somehow my code is not working. The outcome should have been [1, 'zzz', 3, 1, 'zzz', 3]. I attached my code below any help is appreciated. Thanks in advance.
x = [1, 2, 3, 1, 2, 3]
def changeThem(x,y,z):
replace = {y : z}
for key, value in replace.items():
x = x.replace(key, value)
print(x)
changeThem(x,2,'zzz')
A list does not have .replace() method. How about the following?
x = [1, 2, 3, 1, 2, 3]
def changeThem(x,y,z):
return [z if i == y else i for i in x]
print(changeThem(x, 2, 'zz'))
The function consists of just one line so defining this function might not be even necessary. But I am leaving it in case you would like to call it multiple times.
Your code yields an AttributeError. This is because list does not have a replace method. str has a replace method, so this might be where you're getting confused.
You could accomplish this with a very simple list comprehension:
x = [z if e == y else e for e in x]
Essentially, the above list comprehension states:
For every value e in the list x, replace it with z if the element is equal to y. Otherwise, just keep the element there.
It is also equivalent to the following:
result = []
for e in x:
if e == y:
result.append(z)
else:
result.append(x)

Mapping elements using multi-line functions

Consider following example in JavaScript:
var x = [1, 2, 3]
var y = x
.map(i => i * 2)
.map(i => i * i)
If I would like to call some function that does some external-state manipulation, like logging or publishing message to a topic I would simply use the brackets to get multi-line lambda:
var x = [1, 2, 3]
var y = x
.map(i => i * 2)
.map(i => {
console.log("arg received ": + i)
return i * i
})
As far as I know, such a trivial thing is impossible to do in Python because of single line lambdas and we have to use named functions which sucks because any code reader or code reviewer have to jump between function definition and function usage, even though the function is used only once.
Is there any industry standard for solving such a trivial issue?
Specifically for logging or other "void" functions, you could use or:
>>> lst = [1, 2, 3]
>>> list(map(lambda x: x*x, map(lambda x: print(x) or x, map(lambda x: x*2, lst))))
2
4
6
[4, 16, 36]
Here, a or b is equivalent to a if a else b, and since print(x) evaluates to None, which is not "truthy", the "else" part is returned after the print is executed.
Also works as a (nested) list comprehension/generator:
>>> [print(x) or x*x for x in (x*2 for x in lst)]
2
4
6
[4, 16, 36]
Whether that's good style is a different question, though.
Alternatively, and probably a bit cleaner, define a log/peek/debug function that logs the value and then returns it, to be put anywhere in your map/list-comp chain:
>>> def peek(x, f=print):
... f(x)
... return x
...
>>> [x*x for x in map(peek, (x*2 for x in lst))]
2
4
6
[4, 16, 36]
Python doesn't really have this concept, the best you can do is just to define an inner function right above where you'd use it
def my_func(i):
return i * i
y = map(my_func, map(lambda i: i * i, x))

call lambda functions from list

If I have a list containing a lambda function, for example:
a = [lambda x: x * 2]
how could I call this lambda function to a list:
b = [1,2,3,4]
so that it could have the result as:
result = list(map(lambda x: x * 2, b))
and get:
[2,4,6,8]
Further, what if a contains two lambda functions, for example:
a = [lambda x:x * 2, lambda x: x-1]
How could I call these functions to list b without using for loop or list comprehension but use map()?
I hope someone could help me with it!
You can simply do
result = list(map(a[0], b))
# [2,4,6,8]
since the lambda function is the first element of a
For the second function it's the same thing
result = list(map(a[1], b))
If you want to apply all functions in one line
[list(map(a[i],b)) for i in range(len(a))]
#[[2, 4, 6, 8], [0, 1, 2, 3]]
You can iterate over the lambda functions in the list no matter how many elements stored in it:
a = [lambda x:x * 2, lambda x: x-1]
b = [1, 2, 3, 4]
for func in a:
# apply each function on b and turn the results to list:
print(list(map(func, b)))
If you want to store each result:
a = [lambda x:x * 2, lambda x: x-1]
b = [1, 2, 3, 4]
results = []
for func in a:
results.append(list(map(func, b)))
In the special case of a single lambda wrapped in a list, do
result = list(map(a[0], b))
But there are no special cases in python. So for the general case, you can either have a flat list:
result = [f(x) for f in a for x in b]
or a nested one:
result = [[f(x) for f in a] for x in b]
You can reverse the sort order of the lists by swapping the loops over a and b.
You can even go as far as using itertools.product to get the flat case (again with a and b interchangeable to change the order of the result):
result = [f(x) for f, x in itertools.product(a, b)]

Why results of map() and list comprehension are different? [duplicate]

This question already has answers here:
What do lambda function closures capture?
(7 answers)
Closed 6 months ago.
The following test fails:
#!/usr/bin/env python
def f(*args):
"""
>>> t = 1, -1
>>> f(*map(lambda i: lambda: i, t))
[1, -1]
>>> f(*(lambda: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda: i for i in t]) # -> [-1, -1]
[1, -1]
"""
alist = [a() for a in args]
print(alist)
if __name__ == '__main__':
import doctest; doctest.testmod()
In other words:
>>> t = 1, -1
>>> args = []
>>> for i in t:
... args.append(lambda: i)
...
>>> map(lambda a: a(), args)
[-1, -1]
>>> args = []
>>> for i in t:
... args.append((lambda i: lambda: i)(i))
...
>>> map(lambda a: a(), args)
[1, -1]
>>> args = []
>>> for i in t:
... args.append(lambda i=i: i)
...
>>> map(lambda a: a(), args)
[1, -1]
They are different, because the value of i in both the generator expression and the list comp are evaluated lazily, i.e. when the anonymous functions are invoked in f.
By that time, i is bound to the last value if t, which is -1.
So basically, this is what the list comprehension does (likewise for the genexp):
x = []
i = 1 # 1. from t
x.append(lambda: i)
i = -1 # 2. from t
x.append(lambda: i)
Now the lambdas carry around a closure that references i, but i is bound to -1 in both cases, because that is the last value it was assigned to.
If you want to make sure that the lambda receives the current value of i, do
f(*[lambda u=i: u for i in t])
This way, you force the evaluation of i at the time the closure is created.
Edit: There is one difference between generator expressions and list comprehensions: the latter leak the loop variable into the surrounding scope.
The lambda captures variables, not values, hence the code
lambda : i
will always return the value i is currently bound to in the closure. By the time it gets called, this value has been set to -1.
To get what you want, you'll need to capture the actual binding at the time the lambda is created, by:
>>> f(*(lambda i=i: i for i in t)) # -> [-1, -1]
[1, -1]
>>> f(*[lambda i=i: i for i in t]) # -> [-1, -1]
[1, -1]
Expression f = lambda: i is equivalent to:
def f():
return i
Expression g = lambda i=i: i is equivalent to:
def g(i=i):
return i
i is a free variable in the first case and it is bound to the function parameter in the second case i.e., it is a local variable in that case. Values for default parameters are evaluated at the time of function definition.
Generator expression is the nearest enclosing scope (where i is defined) for i name in the lambda expression, therefore i is resolved in that block:
f(*(lambda: i for i in (1, -1)) # -> [-1, -1]
i is a local variable of the lambda i: ... block, therefore the object it refers to is defined in that block:
f(*map(lambda i: lambda: i, (1,-1))) # -> [1, -1]

Categories

Resources