Python: Choose function based on condition in a for loop? - python

Sorry if the title is a little vague. I'll explain everything in greater detail here. So let's say I have this code:
def function1(k):
return k * 2
def function2(k):
return k ** 2
func = 'Square'
for i in range(1, 10):
if func == 'Multiply':
function1(i)
elif func == 'Square':
function2(i)
How can I modify the code above so that the if statement can go outside the loop? It seems unnecessary to check in every iteration the value of func since it's not going to change inside. the loop. What I'm looking for is something like this:
def function1(k):
return k * 2
def function2(k):
return k ** 2
func = 'Square'
if func == 'Multiply':
f = function1()
elif func == 'Square':
f = function2()
for i in range(1, 10):
f(i)
Let me know if something isn't clear enough or if what I'm asking isn't possible.

Store the function in a dict, then you can skip the if statements.
def function1(k):
return k * 2
def function2(k):
return k ** 2
d = {"Multiply": function1, "Square": function2}
func = "Square"
f = d[func]
f(3)
# 9

You can do this:
funcs = {'Multiply':function1, 'Square':function2}
theFunc = funcs[func]
for i in range(1, 10):
x = theFunc(i)
print(x)
One note in passing: the ^ operator is not taking the square of the argument, but rather performing bitwise xor on it.

Related

How is good the practis to compare arguments of function with it's names in Python?

I've made example functions, I know that i will use meta_function only with arguments sum and mult
def sum(a, b):
return a + b
def mult(a, b):
return a*b
def meta_function(function):
if function == sum:
c = 1
print('SUM')
elif function == mult:
c = 2
print("MULT")
print(c)
meta_function(sum)
meta_function(mult)
Output:
SUM
1
MULT
2
PyCharm informing me that Local variable might be referenced before assignment. I know, if some other arguments will be taken except sum and mult, this will lead to error. What is the best practice to handle this sophisticated issue? Try - except? Am I use wright way to take another functions in meta_function?
Pycharm warns you because if the function isn't sum or mult, your code will crash.
You need to define c at the top, just in case, or add an else statement.
# I changed the name of sum_ in order to avoid shadowing with
# the python built-in function sum()
def sum_(a, b):
return a + b
def mult(a, b):
return a * b
def meta_function(function):
c = 0 # if function isn't sum or mult, at least c exist
# here, "is" is better then "=="
if function is sum_:
c = 1
print('SUM')
# here, "is" is better then "=="
elif function is mult:
c = 2
print("MULT")
# Or you can do the else, but since you don't do anything in it,
# the best solution is to define a base state of c
# else:
# c = 0
print(c)
meta_function(sum_)
meta_function(mult)
Here is a better solution in my opinion, in order to prevent changing the core of meta_function() if you want to add more functions:
def sum_(a, b):
return a + b
def mult(a, b):
return a * b
function_switch = {
sum_: ('SUM', 1),
mult: ('MULT', 2),
}
def meta_function(function):
function_res = function_switch.get(function)
if function_res is None:
print(0)
return
print(function_res[0])
print(function_res[1])
meta_function(sum_)
meta_function(mult)
In Python 3.9, you can do:
def meta_function(function):
if (function_res := function_switch.get(function)) is None:
print(0)
return
print(function_res[0])
print(function_res[1])
Warning is shown because if both cases are not happened, then variable c will be not defined and print fill fail. The variable must be defined in the current scope.
It is better to make a generic function, executes the function and returns desired values. I've refactored your function as taking callable function and parameters for given function. Executes given callable and return some string that is built with return results.
def sum(a, b):
return a + b
def mult(a, b):
return a*b
def meta_function(func, *args):
# initialize default variables
c: int = None
return_str: str = None
# try execute given callable `func`
try:
# execute
c = func(*args)
# build some result string and assign
return_str = f"Function:{func.__name__}, Args: {args}, Result: {c}"
except Exception as exp:
# build result string for failed functions
return_str = f"Error calling given function `{func.__name__}` with params: {params}"
return return_str
def incompatible_func(x, y, z): # takes three parameters, will fail
return x + y * z
params = (1,2)
rslt = meta_function(sum, *params)
print(rslt)
rslt = meta_function(mult, *params)
print(rslt)
rslt = meta_function(incompatible_func, *params)
print(rslt)
And the output is:
Function:sum, Args: (1, 2), Result: 3
Function:mult, Args: (1, 2), Result: 2
Error calling given function `incompatible_func` with params: (1, 2)

How to add mathematical function as argument in python function

I know there are similar questions about passing functions into functions, but I'm not clear on the effective solution for my particular problem.
The following function works but the formula is static. It only works on a fixed function, namely (in mathy pseudocode) f(a) = 3^a mod 17 = b where f(11) = 7
def get_a(b):
'''
Get preimage a from A for f(a) = b where b in B
'''
a = 1
while(1):
x = pow(3, a) % 17
if x == b:
return a
if a > 10000:
return -1
a += 1
def main():
b = 7
a = get_a(7)
print(F'The preimage a of b={b} is: {a}')
if __name__ == '__main__':
main()
I want to make a user pass in any given function. To start with, I implemented something just a little more complex but still pretty simple.
def get_a_variable(b, base, mod):
'''
Get preimage a from A for f(a) = b where b in B
'''
a = 1
while(1):
# print(F'a:{a}, b{b}')
x = pow(base, a) % mod
# print(F'x:{x}')
if x == b:
return a
if a > 10000:
return -1
a += 1
def main():
b = 7
a = get_a(7)
print(F'The preimage a of b={b} is: {a}')
a = get_a_variable(7,3,17)
print(F'Again, the preimage a of b={b} is: {a}')
This works but I want to make it even more dynamic.
To try implement this, I created a new function that can be passed as an argument:
def power_mod_function(base, x, mod):
return power(base, x) % mod
I'm not exactly sure how the trial value arg x should be handled or if it should even be in this function.
Then I "forked" the "get_a(b)" function that takes a callback I believe
def get_a_dynamic(b, crypt_func):
'''
Get preimage a from A for f(a) = b where b in B
'''
a = 1
while(1):
x = crypt_func() # Not sure how to manage the arg passing here
if x == b:
return a
if a > 10000:
return -1
a += 1
Then I updated main():
def main():
b = 7
a = get_a(7)
print(F'The preimage a of b={b} is: {a}')
a = get_a_dynamic(b, power_mod_function(3, x, 17)) # Not sure how to pass my middle arg!!
print(F'Again the preimage a of b={b} is: {a}')
I'm getting the following error messages:
python pre_img_finder.py
The preimage a of b=7 is: 11
Traceback (most recent call last):
File "pre_img_finder.py", line 45, in <module>
main()
File "pre_img_finder.py", line 41, in main
a = get_a_dynamic(b, power_mod_function(3, x, 17))
NameError: name 'x' is not defined
I don't know how to set it up right and do the variable passing so that I can pass the static variables once in main and the middle test variable x will always increment and eventually find the results I want.
Perhaps I just need to receive a function that begins with a "type" argument that acts as a kind of switch, and then takes a variable number of arguments depending on the type. For example, we could call the above a base-power-mod function or (bpm), where "power" is the answer we're looking for, i.e. a is the pre-image of b in technical terms. Then just call
main():
a = get_a_dynamic(7, ("bpm", 3,17))
And then implement it that way?
Thanks for your help!
Use *args to pass additional arbitrary number of arguments to the function.
def get_a_dynamic(b, crypt_func, *args):
'''
Get preimage a from A for f(a) = b where b in B
'''
a = 1
while(1):
x = crypt_func(*args)
if x == b:
return a
if a > 10000:
return -1
a += 1
Then call it in your main like this
def main():
b = 7
a = get_a(7)
print(F'The preimage a of b={b} is: {a}')
a = get_a_dynamic(b, power_mod_function, 3, x, 17)
print(F'Again the preimage a of b={b} is: {a}')
The way to generate dynamic functions with partial defined arguments is... functools.partial
from functools import partial
def get_a_variable_with_func(b, func):
'''
Get preimage a from A for f(a) = b where b in B
'''
a = 1
while(1):
x = func(a)
if x == b:
return a
if a > 10000:
return -1
a += 1
b = 7
def power_mod_function(base, mod, x):
return (base ** x) % mod
partial_func = partial(power_mod_function, 3, 17)
a = get_a_variable_with_func(7, partial_func)
print(a)
>>>
11
By the way, this is more pythonic:
from functools import partial
def get_a_variable_with_func(b, func):
'''
Get preimage a from A for f(a) = b where b in B
'''
for a in range(10001):
if func(a) == b:
return a
return -1
def power_mod_function(base, mod, x):
return (base ** x) % mod
a = get_a_variable_with_func(7, partial(power_mod_function, 3, 17))
print(a)
When you do this :
a = get_a_dynamic(b, power_mod_function(3, x, 17))
you don't pass power_mod_function to get_a_dynamic, you pass its result
instead. To pass the function you just have to pass the function name.
Therefore, because the function needs a value internal to get_a_dynamic (the x arg) and also two external arguments (3 and 17), you must pass these two arguments to get_a_dynamic separately for it to be able to call the passed function with the three needed arguments.
For that purpose, the suggestion of AnkurSaxena could be used especially if if the number of args can vary. But you can also declare it like this :
def get_a_dynamic(b, crypt_func, pow, mod):
then use it like this :
a = get_a_dynamic(b, power_mod_function, 3, 17))
You do something like:
def get_a_dynamic(b, function, argtuple):
# …
x = function(*argtuple) # star-operator unpacks a sequence
# … et cetera
… in essence. You can then always check the range of argtuple when passing it around, or be scrupulous about your calling conventions – but that star-operator unpack bit is the crux of what you are looking for, I think.
If you want to pass the name of the function as a string (as per your example) you can do something like:
function = globals()["bpm"] # insert your passed string argument therein
… but that’s kind of sketchy and I don’t recommend it – better in that case to pre-populate a dictionary mapping function string names to the functions themselves.

Pass a variable that increases as it is called?

How can I do this in python?
a = 0
func(a+=1) # a = 1
func(a+=1) # a = 2
Now I have had to solve it like this:
a = 0
a+=1
func(a)
a+=1
func(a)
...
and there must be a better way, right?
Edit:
Actually I also want to be able to pass it to different functions:
a = 0
a+=1
func1(a)
a+=1
func2(a)
a+=1
func1(a)
...
your solution is okay, but if you want to have a counting side effect, use itertools.count object:
import itertools
def f(x):
print(x)
c = itertools.count()
f(next(c))
f(next(c))
or the variant, performing the next call in the functions themselves (itertools.counter is mutable so you can modify it inside the function):
import itertools
def f(c):
x = next(c)
print(x)
c = itertools.count()
f(c)
f(c)
you can initialize it to a non-zero value: c = itertools.count(3)
Something simple like this:
a = 0
a+=1;func1(a)
a+=1;func2(a)
Each line is only 2 characters longer than your original request.
You can have a wrapping function that calls your function f and simultaneously increment the value of j!
>>> def f(a):print("Value of a is: %d"%a)
...
>>> c=lambda i:(i+1,f(i+1))
>>> j=0
>>> j,_=c(j)
Value of a is: 1
>>> j
1
>>> j,_=c(j)
Value of a is: 2
>>> j
2
There is no way in the sense that in Python, assignment is an instruction, not an operator whose operation returns a value.
You'll have to define a function and either use the global keyword with a or turn a into a mutable. You can also use a decorator for your functions.
a = [0]
#a = 0 # alternative version
def inc(x):
x[0] += 1
return x[0]
# global a # alternative version
# a += 1
# return a
def f1(x):
return x + 1
def f2(x):
return x + 2
# or (inspired by #jpp comment) decorate your functions
def inc1(x):
def inner(x):
x[0] += 1
return x[0]
return inner
#inc1
def f3(x):
return x
for i in range(10):
print(inc(a))
print()
print(f1(inc(a)))
print(f2(inc(a)))
print()
a = [0]
print(f3(a))
print(f3(a))

How to compose a list of functions to a callable [duplicate]

This question already has answers here:
Composing functions in python
(16 answers)
Closed 5 years ago.
I have a simple source.
def h(x):
return x + 1
def m(x):
return x + 2
def n(x):
return x * 10
def function_aggregator(fun_list, num):
return_fun = None
for fun in fun_list[::-1]:
if return_fun:
return_fun = fun(return_fun)
else:
return_fun = fun(num)
return return_fun
if __name__ == "__main__":
lst = [h, m, n]
y = function_aggregator(lst, 4)
print(y)
Is there any way to make the function_aggregator method receive just the list and return a callable the will be the same as h(m(n(<any_number>))
The previous answer is pretty close. The exact answer is:
def function_aggregator(fun_list):
def wrapper(arg):
for fun in reversed(fun_list):
arg = fun(arg)
return arg
return wrapper
if __name__ == "__main__":
lst = [g, f, n, m, h]
p = function_aggregator(lst)
x = 3
print("p(x): {}".format(p(x)))
Thanks to Zero Piraeus commet
It could be done using closure:
def function_aggregator(*func_list):
def aggregate(num):
for func in reversed(func_list):
num = func(num)
return num
return aggregate
if __name__ == "__main__":
myfunc = function_aggregator(h, m, n)
print(myfunc(4))
Notes
function_aggregator now takes an arbitrary number of functions
Inside, it defines another function, aggregate, this is the callable you are talking about.
function_aggregator returns that callable, AKA aggregate to the caller
The caller then give it a name, myfunc in this case
From then on, we can treat myfunc as a function which takes 1 argument and return something

Nested Scopes and Lambdas

def funct():
x = 4
action = (lambda n: x ** n)
return action
x = funct()
print(x(2)) # prints 16
... I don't quite understand why 2 is assigned to n automatically?
n is the argument of the anonymous function returned by funct. An exactly equivalent defintion of funct is
def funct():
x = 4
def action(n):
return x ** n
return action
Does this form make any more sense?
It's not assigned "automatically": it's assigned very explicitly and non-automatically by your passing it as the actual argument corresponding to the n parameter. That complicated way to set x is almost identical (net of x.__name__ and other minor introspective details) to def x(n): return 4**n.

Categories

Resources