Define a list of functions from a generic function - python

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

Related

Why can't I use multiple *vars in tuple unpacking on the left-hand side of an assignment?

I am trying to make sense of starred expressions in python. When I use it in python functions, it allows to call functions with different number of arguments:
def my_sum(*args):
results = 0
for x in args:
results += x
return results
print(my_sum(1,2,3))
>>6
print(my_sum(1,2,3,4,5,6,7))]
>>28
However when I use it in an assignment, it works like this:
a, b, *c, d = [1,2,3,4,5,6,7,8,9,10]
print(a,b,c,d)
>>1 2 [3, 4, 5, 6, 7, 8, 9] 10
*a, b, *c, d = [1,2,3,4,5,6,7,8,9,10]
print(a,b,c,d)
*a, b, *c, d = [1,2,3,4,5,6,7,8,9,10]
^
SyntaxError: multiple starred expressions in assignment
Can someone explain to me what is happening behind this assignment that doesn't allow multiple starred expressions?
The language doesn't allow this because it would be ambiguous, and allowing ambiguities is contrary to the Zen of Python:
In the face of ambiguity, refuse the temptation to guess.
Let's say you run:
*a, b, *c, d = [1,2,3,4,5,6,7,8,9,10]
One way to interpret that would be:
a = [1,2,3,4,5,6,7]
b = 8
c = [9]
d = 10
Another would be:
a = [1]
b = 2
c = [3,4,5,6,7,8,9]
d = 10
Yet another would be:
a = []
b = 1
c = [2,3,4,5,6,7,8,9]
d = 10
Python refuses to guess: It simply declares the code invalid.

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

Creating a Python Object from a String in Python 3

This is not a duplicate of creating a function object from a string because that is a Python 2 solution with exec not as a function()
Trying to implement that solution in Python 3 gets this:
d = {}
exec("def f(x): return x")in d
print(d)
returns:
{}
So the question is, how do I create a function in Python 3 from an arbitrary string? (In my case read in from a YAML file, but that's a side issue.)
X in d , returns True if X is found in element d.
Your exec call is defining function f in global scope.
This appears to do what you want it to:
>>> d = {}
>>> exec("def f(x): return x", d)
>>> d["f"]("Hello World")
'Hello World'
>>> d={}
>>> exec("def f(x): return x",None,d)
>>> d['f'](2)
2
>>>

Altering the definition of a function without an assignment

I was given this task:
Write a one-line expression that transforms an f(x) function into f(x)
+1. Hint: think about how a local frame binding for saved value of f, can be created without an assignment.
Example:
>>> f = lambda x: x*x
>>> f(5) 25
>>> ---your one line expression---
>>> f(5) 26
>>> f, g = None, f
>>> g(5) 26
I tried to do this:
k,f=f, lambda x: k(x)+1
And it works but it uses the assignment f=f. How could I do this without an assignment?
My teacher told me that there is a function in Python similar to the let function in Scheme, but I'm not sure what this function is that she wants us to use because she did not provide the name of the function.
This is not something that should be used in general, but I suppose you could do this:
def f(x, f=f): return f(x) + 1
And there's "no (explicit) assignments" since we're just defining a new function name which happens to be the same as the original.
This will work, but it may be cheating:
>>> f = lambda x: x*x
>>> f(5)
25
>>> globals().update({'f': (lambda g: lambda x: g(x)+1)(f)})
>>> f(5)
26
>>> f, g = None, f
>>> g(5)
26

Python lambda parameters

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

Categories