Python lambda parameters - python

I have the following code:
g = lambda a, b, c: sum(a, b, c)
print g([4,6,7])
How do I get the lambda function to expand the list into 3 values?

Expand the list t0 3 values can be done by this:
g(*[4,6,7])
But the sum won't work in your way.
Or you can write this way:
>>> g = lambda *arg: sum(arg)
>>> print g(4, 5, 6)
15
>>>
Or just make your lambda accept a list:
g = lambda alist: sum(alist)
print g([4,6,7])

Why can't you change lambda to take a list. Because sum() doesn't take three arguments:
>>> g = lambda a_list: sum(a_list)
>>> print g([4,6,7])
17
or a non-keyword argument:
>>> g = lambda *nkwargs: sum(nkwargs)
>>> print g(4,6,7)
17

g = lambda L: sum(L)
print g([4,6,7])
would work for any arbitrarily sized list.
If you want to use g = lambda a, b, c: someFunc(a, b, c), then call print g(4,6,7)

You're defining a function which takes three parameters, and the supplying it with a single parameter, a list.
If you want
print g([4,6,7])
to work, your definition should be
g = lambda lst: sum(lst)

The code you are looking for is:
>>> g = lambda a, b, c: sum([a, b, c])
>>> print g(*[4,6,7])
17
What you were trying to do wont work:
>>> g = lambda a, b, c: sum(a, b, c)
>>> print g(*[4,6,7])
Traceback (most recent call last):
File "<pyshell#83>", line 1, in <module>
print g(*[4,6,7])
File "<pyshell#82>", line 1, in <lambda>
g = lambda a, b, c: sum(a, b, c)
TypeError: sum expected at most 2 arguments, got 3
Because sum() can't handle the arguments you gave it.
Since your lambda function is simply a sum() function, why not just call sum() directly?
If your code the following a, b, c values:
>>> a, b, c = range(1,4)
>>> print a,b,c
1 2 3
And you wanted to do:
>>> g = lambda a, b, c: sum([a, b, c])
>>> print g(*[a,b,c])
6
Why not just do the following?:
>>> sum([a,b,c])
6

>>> g=lambda *l: sum(l)
>>> print g(1,2,3)
6

If your lambda expects to have a list/tuple of fixed length passed in, but wants to expand the values in that list/tuple into separate parameter variables, the following will work.
g = lambda (a, b, c): a + b + c
g([4, 6, 7])
Note the parentheses around the parameter list.
This "feature" works in Python 2.x, but was removed in Python 3

Related

An error when redirecting a list of objects to reduce function with lambda

Here is the simple code.
class C:
def __init__(self):
self.l = [1,2,3]
a = C()
b = C()
c = C()
reduce(lambda x,y: x.l + y.l, [a,b,c])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'list' object has no attribute 'l'
I know there are several other ways. But I want to see why this doesn't work.
The output should be the same given by
sum([x.l for x in [a,b,c]], [])
which is
[1, 2, 3, 1, 2, 3, 1, 2, 3]
The function passed as the first argument to reduce takes 2 arguments, the first is the result of the previous reduction (or the first item in the iterable if no initial value is given). In your case, after the first 2 lists have been added together x is now a list when passed to the lambda for the second iteration and doesn't have an attribute l as it is not an instance of C
If you run your list through map you can extract the list from all the objects and pass them to the reduce
reduce(lambda x, y: x + y, map(lambda z: z.l, [a,b,c]))
As Iain says in his answer, the problem hits after the first term is evaluated, since your types stop matching.
Reduce is of the form: reduce(f(B,A)->B, [A], optional B) -> B
You provide a function that has a signature of f(C, C) -> [int]
reduce(lambda x,y: x.l + y.l, [a,b,c])
which doesn't match f(B,A)->B. You can clearly fix this in two ways, by changing the first argument (1) or the return (2)
(1) f(int, C) -> int. Where A is C and B is [int].
reduce(lambda x, y: x + y.l, [a, b, c], [])
(2) f(C, C) --> C. Where A is C and B is also C.
def add_c(c1, c2):
ret = C()
ret.l = c1.l + c2.l
return ret
reduce(add_c, [a, b, c]).l
Note the .l to extract your list
Note that the second is only ugly because your class is pretty silly, and has no init. Also the lowercase L as a variable is unfortunate.

Lambda function - Unknown number of arguments

Currently, this is how I resolve a "and" function using lambda with two arguments:
custom_function = lambda a, b: a and b
But how can I resolve an unknown number of arguments, like:
custom_function = lambda a, b, c, d, ...: what now?
Anybody had this issue before?
Thanks and Greetings!
You can use "*args":
>>> custom_function = lambda *args: all(args)
>>> custom_function(1, 2, 3)
True
>>> custom_function(1, 2, 3, 0)
False
Which indeed is the same as just using "all":
>>> all(1, 2, 3)
True
>>> all(1, 2, 3, 0)
False
To be general, you can use "functools.reduce" to use any "2-parameters" function with any number of parameters (if their order doesn't matter):
import operator
import functools
c = lambda *args: functools.reduce(operator.and_, args)
(same results as before)
You can use argument unpacking via the * operator to process any number of arguments. You would have to resort to reduce (Python2) or functools.reduce (Python3) in order to combine them all with and in a single expression (as needed by the lambda):
from functools import reduce # only Py3
custom_function = lambda *args: reduce(lambda x, y: x and y, args, True)
Note: this is not the same as all, like many here suggest:
>>> all([1,2,3])
True
>>> 1 and 2 and 3
3
>>> custom_function(1,2,3)
3
First, use *args to store an unknown number of arguments as a tuple.
Second, all(args) only return Ture or False but and operation may return value (Here is why). So we need to use reduce.
Here is the solution:
custom_function = lambda *args: reduce(lambda x,y: x and y, args)
Test 1: arguments are Ture or False
>>> custom_function(True,False,True,False)
False
>>> custom_function(True,True,True)
True
Test 2: arguments are values
>>> custom_function(1,2,3,4,3,2)
2
>>> custom_function('a','b','d','s')
's'
Test 3: arguments are a combination of bool and values
>>> custom_function(1,2,True,4,3,2)
2
>>> custom_function(1,2,False,4,3,2)
False
Note the three tests are correct according to the definition of Logical AND (and):
Return the first Falsey value if there are any, else return the last
value in the expression.
Why not just using the all function?
a = 1
b = 2
c = None
args = [a, b, c]
print (all(args))
# False

Python: Concatenation of two Lists behaving weird

I am very new to Python, and I wrote the below code which returns the kth row of a Pascal triangle where k is supplied as 'rowIndex' parameter. The problem occurs in the map function where the concatenation [0]+row is working fine but row+[0] is not working and an error is being thrown as shown below. This problem didn't occur in the code which is implemented using zip instead of map (which is commented out in the below code) Can anyone point out what the problem is?
Thanks in advance!
Error:
row = [map(lambda a, b: a + b, [0]+row, row+[0])]
TypeError: unsupported operand type(s) for +: 'int' and 'list'
Code:
class Solution(object):
def getRow(self, rowIndex):
"""
:type rowIndex: int
:rtype: List[int]
"""
row = [1]
for _ in range(rowIndex):
row = [map(lambda a, b: a + b, [0]+row, row+[0])]
# row = [x + y for x, y in zip([0]+row, row+[0])]
return row
obj = Solution()
print obj.getRow(3)
map returns a list, you should not wrap it with [...].
>>> map(lambda a, b: a + b, [0,1,2], [1,2,0]) # Python 2.x
[1, 3, 2]
By wrapping it with [..], you will get a list of a list; which will cause TypeError after the first iteration:
>>> [map(lambda a, b: a + b, [0,1,2], [1,2,0])]
[[1, 3, 2]]
row = map(lambda a, b: a + b, [0]+row, row+[0])
If you are using Python 3.x, map returns an iterator; you need to convert it to list using list function to get a list object:
>>> map(lambda a, b: a + b, [0,1,2], [1,2,0]) # Python 3.x
<map object at 0x7f5acef0a0b8>
>>> list(map(lambda a, b: a + b, [0,1,2], [1,2,0]))
[1, 3, 2]
row = list(map(lambda a, b: a + b, [0]+row, row+[0]))

Iterate a tuple with dict inside

Having trouble iterating over tuples such as this:
t = ('a','b',{'c':'d'})
for a,b,c in t:
print(a,b,c) # ValueError: not enough values to unpack (expected 3, got 1)
for a,b,*c in t:
print(a,b,c) # ValueError: not enough values to unpack (expected 2, got 1)
for a,b,**c in t:
print (a,b,c) # Syntax error (can't do **c)
Anyone know how I can preserve the dictionary value? I would like to see a='a', b='b', and c={'c':'d'}
You'd need to put t inside some other iterable container:
for a, b, c in [t]:
print(a, b, c)
The problem with your attempts is that each on is iterating over a single element from t and trying to unpack that. e.g. the first turn of the loop is trying to unpack 'a' into three places (a, b and c).
Obviously, it's also probably better to just unpack directly (no loop required):
a, b, c = t
print(a, b, c)
Why are you iterating at all when it's a single tuple? Just unpack the single tuple, if that's what you need to do:
a, b, c = t
print(a, b, c)
Or if it's just printing you want to do unpack in the call itself:
print(*t)
Try this:
for a,b,c in [t]:
print(a,b,c)
Putting t inside a list will allow you to unpack it.
You can use tuple unpacking with multiple assignment:
a, b, c = t
print(a, b, c)
try this
for x in t:
print(x)
x will take all the values in t iteratively, so 'a', then 'b' and finally {'c':'d'}.
And to print exactly a = 'a' etc you can do:
for param, val in zip(["a","b","c"], t):
print(param,"=",val)

Define a list of functions from a generic function

Say I have a function
def my_meta_function (a, b, c):
pass
I would like define an array of functions myfunctions = [f1, f2, f3, ... f100], where the argument c is fixed to a different value for each such function, e.g. c = [1,2,3, .. 100], and the functions only take the arguments a and b. In practice, the arguments I am considering are more complictated, but I am trying to understand how to do this in the language.
Does this type of meta-programming have a name?
Are decorators appropriate for this? If not, why?
Use functools.partial:
>>> from functools import partial
def func(a, b, c):
... print a, b, c
...
>>> funcs = [partial(func, c=i) for i in xrange(5)]
>>> funcs[0](1, 2)
1 2 0
>>> funcs[1](1, 2)
1 2 1
>>> funcs[2](1, 2)
1 2 2
Just for learning purpose you can also do this using lambda:
>>> funcs = [lambda a, b, i=i:func(a, b, c=i) for i in xrange(5)]
>>> funcs[0](1, 2)
1 2 0
>>> funcs[1](1, 2)
1 2 1
But you should not use it, why?:
Python: Why is functools.partial necessary?
What do (lambda) function closures capture in Python?
You could do this:
>>> def func(c):
... def wrapped(a, b):
... return (a+b)*c
... return wrapped
...
>>> funcs = [func(c+1) for c in xrange(100)]
>>> funcs[0](1, 2)
3
>>> funcs[1](1, 2)
6

Categories