I'm trying to initialize a collection of function calls based on input data, but not sure how to do this. The ideas is like below. I expect the local_v will stay unchanged once being defined. Can anyone shed some light how to deal with this or where to start with?
ls = [1,2]
for v in ls:
def fn():
local_v = v
print(local_v)
locals()[f'print{v}'] = fn
print1() # output: 2 expect to be 1
print2() # output: 2
What you're trying to create is called a "closure", which is a special type of function that retains the values of its free variables. A closure is created automatically when free variables go out of scope. In your example, since you never leave the scope in which v is defined, your fn objects are ordinary functions, not closures (you can verify that by inspecting their __closure__ property).
To actually create a closure you need to construct a new scope, inject your loop variable into that scope and then leave that scope. In python, a new scope is created for defs, lambdas, comprehensions and classes. You can use any of these, for example, given
ls = [1, 2, 3, 4, 5]
fns = []
you can use a helper function:
def make_fn(arg):
def f():
print(arg)
return f
for item in ls:
fns.append(make_fn(item))
for fn in fns:
fn()
or a lambda:
for item in ls:
fn = (lambda x: lambda: print(x))(item)
fns.append(fn)
or a (pointless) list comprehension:
for item in ls:
fn = [
lambda: print(x)
for x in [item]
][0]
fns.append(fn)
As a side note, modifying locals is not a good idea.
If you want v to be tied to the local context, you'll have to pass it inside the function. You will also need to return a callable to print rather than using the print function directly, or else you will find that print1 and print2 will be None. And at this point, there is no need to redefine fnat each iteration of the loop. Something like that will work:
ls = [1, 2]
def fn(v):
return lambda: print(v)
for v in ls:
locals()[f'print{v}'] = fn(v)
print1() # output: 1
print2() # output: 2
Just to try to be clearer: invoking print1() does not call fn(1), it calls the lambda that returned by fn(1).
Better to store the functions in a dictionary rather than changing locals, which is likely to get confusing. You could use a nested function like this:
def function_maker(v):
def fn():
print(v)
return fn
print_functions = {v: function_maker(v) for v in [1, 2]}
print_functions[1]()
print_functions[2]()
Alternatively use functools.partial
from functools import partial
def fn(v):
print(v)
print_functions = {v: partial(fn, v) for v in [1, 2]}
Related
Given there is a list of function's names as strings, would it be possible to call corresponding function with random sampling from the list? Currently I am hard coding all the name by if statement so that
def a(x):
print(x)
def b(y):
print(y)
# there are more functions
func_list = ['a', 'b', ...] # all function's names are stored
chosen_function = random.choice(func_list)
if chosen_function == 'a':
a(1)
elif chosen_function == 'b':
b(2)
# elif continues...
I want to eliminates all if statements so that whenever I add new functions in func_list, I do not need to modify the if statements.
However, I can not contain the function itself in the list as the actual functions have randomness in them so I need to call it after the function is sampled.
The answer of #Jarvis is the right way to go, but for completeness sake, I'll show a different way: inspecting the global variables.
def a(x):
print(x)
def b(y):
print(y)
# there are more functions
func_list = ['a', 'b', ...] # all function's names are stored
chosen_function = random.choice(func_list)
globals()[chosen_function](x)
Why not use a dictionary?
def a(x):
print(x)
def b(y):
print(y)
func_dict = {'a': a, 'b': b}
Call it like this:
x = 3 # your int input
func_dict['a'](x) # calls a(x)
func_dict['b'](x) # calls b(x)
If you want to directly specify arguments, you can do so in the dictionary like this:
func_dict = {
'a': lambda: a(1),
'b': lambda: b(2)
}
Call the default methods like:
func_dict['a']() # calls a(1)
func_dict['b']() # calls b(2)
You may consider using eval function. In a nutshell, it evaluates a string into a Python snippet.
For example, in the below code, the eval function evaluates any as a string and interprets to python's any built-in function.
>>> eval('any')
>>> <built-in function any>
On similar grounds, you could evaluate the intended function from a string as below.
def function_a():
print('function A')
def function_b():
print('function B')
function_to_run = eval('function_b') # function_to_run is a callable now
function_to_run()
Result
function B
you can set the default value in the function itself
def a(x=1):
print(x)
def b(y=2):
print(y)
chosen_function = random.choice(func_list)
chosen_function()
I have changed my question a bit, I think my question was not clear before. Apologies!!!
I have mentioned two cases below: one with lambda function and the other with a user defined function.
Case 1: Using a lambda function.
[lambda i: i*2 for i in range(4) ]
The output is the list of 4 same functions as given below:
[<function __main__.<listcomp>.<lambda>(i)>,
<function __main__.<listcomp>.<lambda>(i)>,
<function __main__.<listcomp>.<lambda>(i)>,
<function __main__.<listcomp>.<lambda>(i)>]
Case 2: Using a user defined function
def func(x):
return(x*2)
[func(i) for i in range(4) ]
The output here is as intended.
[0, 2, 4, 6]
Your difference is because
lambda i: i*2
is a function definition, not a function call. To see this, do
func = lambda i: i*2
print(func)
So your list comprehension is defining 4 identical functions. But you want to define one function and call it 4 times. This will work:
[(lambda x: x*2)(i) for i in range(4) ]
Compare that with:
[func(i) for i in range(4) ]
and you will see that (lambda x: x*2) is the equivalent of func. The identifier func is the name of a function that you have defined before, and (lambda x: x*2) is an expression that defines the function in-line.
Here x is the parameter to the lambda function (just as it is in func) and i is the value you are calling that function with.
But that is a very complicated way to to things. Defining a lambda function made it hard for you to see what was wrong, and hard for others to see what your intent was.
I have a list: mylist = [1,2,5,4,7,8]
I have defined a number of functions that operate on this list. For example:
def mean(x): ...
def std(x): ...
def var(x): ...
def fxn4(x): ...
def fxn5(x): ...
def fxn6(x): ...
def fxn7(x): ...
Now I am given a list of function names that I want to apply on mylist.
for ex: fxnOfInterest = ['mean', 'std', 'var', 'fxn6']
What is the most pythonic way to call these function?
I don't think that there is a pythonic™ way to solve the question. But in my code it's quite a common situation, so I've written my own function for that:
def applyfs(funcs, args):
"""
Applies several functions to single set of arguments. This function takes
a list of functions, applies each to given arguments, and returns the list
of obtained results. For example:
>>> from operator import add, sub, mul
>>> list(applyfs([add, sub, mul], (10, 2)))
[12, 8, 20]
:param funcs: List of functions.
:param args: List or tuple of arguments to apply to each function.
:return: List of results, returned by each of `funcs`.
"""
return map(lambda f: f(*args), funcs)
In your case I would use it the following way:
applyfs([mean, std, var, fxn4 ...], mylist)
Note that you really don't have to use function names (as you would have to do in, for example, PHP4), in Python function is the callable object by itself and can be stored in a list.
EDIT:
Or possibly, it would be more pythonic to use list comprehension instead of map:
results = [f(mylist) for f in [mean, std, var, fxn4 ...]]
You can get the functions by their names with:
map(globals().get, fxnOfInterest)
And then loop over them and apply them to the list:
[func(mylist) for func in map(globals().get, fxnOfInterest)]
You can use eval
mylist = [1,2,5,4,7,8]
fxnOfInterest = ['mean', 'std', 'var', 'fxn6']
for fn in fxnOfInterest:
print eval(fn+'(mylist)')
Try this example , I think nothing could be more pythonic than this ,
I call it a function dispatcher.
dispatcher={'mean':mean,'std':std,'var':var,'fxn4':fxn4}
try:
for w in fxnOfInterest :
function=dispatcher[w]
function(x)
except KeyError:
raise ValueError('invalid input')
Each time function will get value according to dispatcher dictionary,
When you in Rome do Like Romans .
def foo(a, l=[]):
l.append(a)
return l
print foo(10)
Result: [10]
print foo(20)
Result: [10, 20]
Why is this happening?
This line:
def foo(a, l=[]):
is creating a list at definition time.
That same list is being used every time the function is called.
Normally something like def foo(a, l="some string"): would work fine, but that's because string literals, integers and such are immutable and therefore it doesn't matter if every instance of the function accesses the same object in memory. Lists, on the other hand, are mutable.
You can get around this like so:
def foo(a, l=None):
if l is None:
l = []
l.append(a)
return l
This also creates a new list, but only upon execution of the function, in local scope. It won't reuse the same one.
When you give a default value to a function argument in python, it gets initialized only ONCE.
That means even if you call your foo() a million times, you are appending to the SAME list.
print foo(10)
print foo(20, [])
output:- [20]
this time you are sending another list reference so value will be inserted in another list
reference.
If you do
def foo(a, l=[]):
print id(l)
...
...
print foo(10)
print foo(20)
both time reference is same
140403877687808
[10]
140403877687808
[10, 20]
while in another case:-
def foo(a, l=[]):
print id(l)
...
...
print foo(10)
print foo(20, [])
reference changes:-
>>>
140182233489920
[10]
140182233492512
[20]
It's because
Default parameter values are always evaluated when, and only when,
the “def” statement they belong to is executed.
“def” is an executable statement in Python, and that default arguments are
evaluated in the “def” statement’s environment. If you execute “def” multiple times,
it’ll create a new function object (with freshly calculated default values) each time.
We’ll see examples of this below.
So here In your code, You are defining the list as default parameters. So it is evaluated only once when function is defined. Here come the reference of "def"
So only one object of list is created which is used multiple times whenever function is called and items get appended.
If you look the list identity then you will find function keeps returning the same object
>>> def foo(l=[]):
... l.append(1)
...return l
>>> id(foo())
12516768
>>> id(foo())
12516768
>>> id(foo())
12516768
The way to achieve what you want to do is
def foo(a, l=None):
if l is None:
l = []
#Do your stuff here with list l
Is there a way to attach a function (same function) to all the elements of an array without looping through and attaching it one by one?
So like
# create function foo from some computation
foo # some def
# list
objects # list of objects
# attach same foo function to all elements of objects
# maybe using a decorator?
# loop through list to execute foo
for obj in objects:
obj.foo()
Let me explain this more:
Of course I can just assign the value of an object like
obj.attr = value
or for an object list:
for obj in objects:
obj.attr = value
What I am trying to avoid is setting the value of an attr on each single object, but rather applying a function on the entire list/array and each element would execute that function.
You could make a function to wrap it up:
def for_each(l, f):
for item in l:
f(item)
Then for a function foo you could do this:
for_each(objects, foo)
For a method foo you could do this:
for_each(objects, lambda item: item.foo())
Or this:
from operator import methodcaller
for_each(objects, methodcaller('foo'))
In Python 2, you can also use map:
map(foo, objects)
For Python 3, you'll have to wrap that in list(...). In either version, you can use list comprehensions:
[foo(item) for item in objects]
However, if you're calling the function just for its side effect rather than transforming the list somehow, I'd recommend against these last two ways as it's against the Zen of Python:
Explicit is better than implicit.
And frankly, one more line for a for loop isn't that much.
You can use map. It is generally used to create a second list, and will return that value, but you can just ignore it.
map(lambda x: x.foo(), objects)
use numpy vectorize! It will work perfectly for you!
from numpy import np
def fun(x):
#do something
array = np.array(your_list)
vectfun = np.vectorize(fun)
answer = vectfun(array)
So now answer will be a resulting array consisting of all the items in the previous list with the function applied to them!! Here is an example:
>>> your_list = [1,2,3,4,5]
>>> def fun(x):
... return x**x
...
>>> array = np.array((your_list))
>>> vectfun = np.vectorize(fun)
>>> answer = vectfun(array)
>>> answer
array([ 1, 4, 27, 256, 3125])