How to iterate over functions with a different number of arguments - python

I want to iterate over a number of functions that are stored in a dictionary as :
function_dict = {'func_a': partial(func_a),
'func_b': partial(func_b)}
Let's say for simplicity that these functions are:
def func_a(x,y):
return x + y
def func_b(x):
return x**2
In fact, these functions are part of an external library, so I cannot modify them.
I want to be able to loop through all of them. However, func_a receives 2 arguments while func_b receives 1 argument.
I want to do the following:
x = 2
y = 5
for key, func in function_dict.items():
if (key=='func_a'):
print( (func)(x,y) )
if (key=='func_b'):
print( (func)(x) )
which yields
7
4
In this loop I have used the if-statement to see which function is coming up and use the appropriate number of arguments. But this is unusable if there are too many functions in the function_dict.
How can i standardize it so that I can loop through all the functions in the dictionary and pick up the correct number of arguments for each of them ?
Many thanks in advance.

Related

Update Global Variables Input as Parameters Rather Than Returning Results From Function

Goal
I am trying to write a function where one or more of the input parameters is a global variable that is updated by the function, without having to return values from within the function. I am aware I could just return a tuple or two separate values from the function, but I think updating the global variables from within the function would be another interesting method if it is possible.
Reason to do this
Updating global variables with a function is easy when the global variable is known (ie. defined previously in the python script). However, I want to define the function in a separate .py file to easily use the function within other python scripts. Therefore, I need to be able to support different variable names to update.
While this is not at all necessary, I am just interested if this is even possible.
Example Pseudocode
I'm thinking something like this:
def math_function(input_val, squared_result, cubed_result):
squared_result = input_val**2 #update the var input as the squared_result parameter
cubed_result = input_val**3 #update the var input as the cubed_result parameter
where you would input a number for input_val and then global variables for squared_result and cubed_result that the function updates with the result. It would then theoretically work like:
#Declare global variables
b = 0
c = 0
#then somewhere in the code, call the function
math_function(2, b, c)
#check the new values
print(b) #Output: b = 4
print(c) #Output: c = 8
This would allow me to use the function in different python scripts without having to worry about what order the results are returned in.
First: I am in no way advocating this.
You could use the globals builtin function to access a global variable by name:
def gtest(name,value):
globals()[name] = value
gtest('new_global','new_value')
print(new_global)
I am not advocating this too, I would like to hear everyone's thoughts, but this can be done -
my_global = [0, 0]
def my_math_func(x, g):
g[0] = x ** 2
g[1] = x ** 3
my_math_func(3, my_global)

Decorating on function defined within loop [duplicate]

This question already has answers here:
Creating functions (or lambdas) in a loop (or comprehension)
(6 answers)
Closed 1 year ago.
I believe this is a similar case, however, I'm not sure how to apply the solutions to my case.
I am creating a function within a loop that is supplied by an argument. The function is called later, but it is stored for later and is supplied with the argument.
for type_str in ["type_1", "type_2", "type_3"]:
#decorator_exec.register(...)
def to_exec(num: int):
print(type_str, num)
# later...
# this is an example
i = 0
for func in decorator_exec.funcs:
func(i)
i++
Unfortunately, this is what would be produced:
type_3 0
type_3 1
type_3 2
I would like the output to ensure that the type_str variable (and any other variables that are included in to_exec() body), be what it should be when it is defined.
type_1 0
type_2 1
type_3 2
The to_exec() that is within the loop isn't used later within the loop, or outside the loop. It is solely stored by the decorator code and is called from its stored functions later.
If it isn't an XY problem, a possible solution would be to use a function factory (I'm not sure how that would work...)
You are not storing the functions for later, you are assigning three functions to the same name, so the last one is what sticks: the previous two get overwritten by the next one.
You can achieve what you're looking for with something like this:
# This is a dictionary created with a dict comprehension.
functions = {
# A decorator is just a function that takes a function and
# returns a function, so you can call it like a normal
# function. The first argument is your own function,
# and the result will be the decorated function.
type_str: decorator_exec.register(
# This is an anonymous function. You don't need to give
# a name to the function in this case because it will
# be called with the dict key it's stored in anyway.
lambda num: print(type_str, num),
..., # Any arguments the decorator needs
)
for type_str in ["type_1", "type_2", "type_3"]
}
This will create a dictionary with one entry per string in your list, such that functions["type_1"] will have the function defined with "type_1" and so on, which you can call, for example, like this: functions["type_2"](420).
You could achieve the same by adding each entry to an empty dict using a for loop, but comprehensions are usually more idiomatic. Because of the decorator, in this case it's probably more readable to do it the wordy way (and it allows you to use type annotations too):
functions = {}
for type_str in ["type_1", "type_2", "type_3"]:
#decorator_exec.register(...)
def the_func(num: int):
print(type_str, num)
functions[type_str] = the_func
The key takeaway is that you cannot use the same name more than once and expect different results, so you have to store your functions in a data structure. If you don't care about the name of the functions at all, you can use a list instead of a dictionary, and simply retrieve/call each function with its index.
You need to keep the reference to the correct string in memory. you can do that by accessing the items with an index variable.
The 2 first lines are relevant for this.
The rest is just to provide a working example,
def generate_funcs(xs, i=0):
return [lambda x: print(xs[i], x)] + generate_funcs(xs, i+1) if i<len(xs) else []
f = generate_funcs(["type_1", "type_2", "type_3"])
for (func, i) in zip(f, range(len(f))):
func(i)

Passing some values as variables

I'm a physics graduate student with some basic knowledge of Python and I'm facing some problems that challenge my abilities.
I'm trying to pass some variables as dummies and some not. I have a function that receives a function as the first argument, but I need that some values to be declared "a posteriori".
What I'm trying to mean is the following:
lead0 = add_leads(lead_shape_horizontal(W, n), (0, 0, n), sym0)
The function "add_leads" takes some function as well as a tuple and a third argument which is fine. But n hasn't any definition yet. I want that n has an actual sense when it enters "add_leads".
Here is the actual function add_leads
def add_leads(shape, origin_2D, symm):
lead_return = []
lead_return_reversed = []
for m in range(L):
n = N_MIN + m
origin_3D = list(origin_2D)+[n]
lead_return.append(kwant.Builder(symm))
lead_return[m][red.shape(shape(n), tuple(origin_3D))] = ONN + HBAR*OMEGA*n
lead_return[m][[kwant.builder.HoppingKind(*hopping) for
hopping in hoppings_leads]] = HOPP
lead_return[m].eradicate_dangling()
Note that n is defined under for, so, I wish to put the value of n in shape(n) (in this case leads_shape_horizontal with a fixed value for W, not for n).
I need this to be this way because eventually the function which is the argument for lead_shape might have more than 2 input values but still just need to vary n
Can I achieve this in Python? If I can, How to do so?
Help will be really appreciated.
Sorry for my english!
Thanks in advance
You probably should pass in the function lead_shape_horizontal, not the function with argument lead_shape_horizontal(W, n)
Because the latter one will return the result of the function, not function object itself. Unless the return value is also a function, you'll get an error when you later call shape(n), which is identical to lead_shape_horizontal(W, n)(n)
As for providing a fix value for W but not for n, you can either give W a default value in the function or just don't make it an argument
For example,
def lead_shape_horizontal(n, W=some_value):
# do stuff
or If you always fix W, then it doesn't have to be an argument
def lead_shape_horizontal(n):
W = some_value
# do stuff
Also note that you didn't define n when calling function, so you can't pass in n to the add_leads function.
Maybe you have to construct the origin_2D inside the function
like origin_2D = origin_2D + (n,)
Then you can call the function like this lead0 = add_leads(lead_shape_horizontal, (0, 0), sym0)
See Python Document to understand how default value works.
Some advice: Watch out the order of arguments when you're using default value.
Also watch out when you're passing in mutable object as default value. This is a common gotcha

Python A function that takes a function as a parameter with its parameters

Imagine I have a two functions
def areaSquare(a,b):
print( a * b)
def areaCircle(radius):
print(3.14159 * radius ** 2)
And I want to create a third function that is called area.
area(areaCircle,radius = 3, repeat = 5)
# prints 3.14159 * 9 five times
area(areaSquare, a = 2, b = 3, repeat = 6)
# prints 2 * 6 six times
So the function takes a function as a parameter. Depending on the function which is passed to it as a parameter, it should require additional parameters. Is there a way to achieve this? I know function overloading would be an option. But I do not want to define multiple functions for this purpose.
Yes, kwargs are your friend here. We can define the function such that the remaining parameters are captured in a dictionary named kwargs, and then passed to the function we provide. Like:
def area(func, repeat, **kwargs):
for _ in range(repeat):
func(**kwargs)
So all parameters except func and repeat are stored in the kwargs dictionary, and later we call the func function (the first argument) with the named parameters.
Note that this will not work (correctly) for functions that require a func and/or repeat parameter, since we capture these at the area function level.

Can one use closures to simplify functions in Python?

Imagine if you want to make a closure function that decides some option for what its inner function does. In this example, we have an inner function that decides whether a number is even, but the generator decides whether the number zero is even, as if there were a debate.
def generate_is_even(reject_zero):
def is_even(x):
return (x % 2 == 0 and x != 0) if reject_zero else x % 2 == 0
return is_even
If is_even(x) is run millions of times, it appears reject_zero will still be checked every single time is_even(x) is run! My actual code has many similar 'options' to create a function that is run millions of times, and it would be inconvenient to write functions for every combination of options. Is there a way to prevent this inefficiency, or does some implementation of Python simplify this?
You seem to be looking for something like macros in C. Unfortunately, Python is not compiled (not the same way as C, for purists), and I don't see direct solutions for your need.
Still, you could set all your parameters at the beginning of runtime, and select the functions at this moment according to the values of the parameters. For instance, your function generator would be something like:
def generate_is_even(reject_zero):
def is_even_true(x):
return (x % 2 == 0 and x != 0)
def is_even_false(x):
return x % 2 == 0
return (is_even_true if reject_zero else is_even_false)
def setup(reject_zero, arg2, arg3):
is_even = generate_is_even(reject_zero)
The backlash of this is having to write a generator for each function that handles such a parameter. In the case you present, this is not a big problem, because there are only two versions of the function, that are not very long.
You need to ask yourself when it is useful to do so. In your situation, there is only one boolean comparison, which is not really resource-consuming, but there might be situations where generating the functions before could become worthwhile.
consider caching all your options in a list, and the generated function only iterates the chosen function
def generate_is_even(**kwargs):
options = {'reject_zero': lambda x: x != 0}
enabled = [options[o] for o in options if o in kwargs and kwargs[o]]
def is_even(x):
return all([fn(x) for fn in enabled]) and x % 2 == 0
return is_even
then you could use
is_even_nozero = generate_is_even(reject_zero=True)
is_even_nozero(0) # gives False
is_even = generate_is_even()
is_even(0) # gives True
if you need add options then add it to the options dict, and you could usee new_option=True is the generate_is_even function to enable it

Categories