How to add mathematical function as argument in python function - python

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.

Related

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

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.

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)

What is the syntax for the input for a def function with multiple nested functions?

I'm learning Python right now and I am just trying to get to grips with all of the syntax options.
Currently, the only thing that I can't seem to google up is what to do if I for some reason want to define a function which contains multiple other defines.
While I understand what to do if there's only 1 define inside the the larger define (val = f()(3,4) returns 7 if you exclude the second def below), I don't know how to correctly use the function below.
If it's possible, what is the syntax for a def function with an arbitrary amount of defined functions within it?
Code:
def f():
def x(a,b):
return a + b
return x
def y(c,d):
return c + d
return y
val = f()(3,4)(5,6)
print(val)
I expected the above to return either (7,11) or 11. However, it returns 'int object is not callable'
When you write val = f()(3,4)(5,6), you want f to return a function that also returns a function; compare with the simpler multi-line call:
t1 = f()
t2 = t1(3,4)
val = t2(5,6)
The function f defines and returns also has to define and return a function that can be called with 2 arguments. So, as #jonrsharpe said, you need more nesting:
def f():
def x(a, b):
def y(c, d):
return c + d
return y
return x
Now, f() produces the function named x, and f()(3,4) produces the function named y (ignoring its arguments 3 and 4 in the process), and f()(3,4)(5,6) evaluates (ultimately) to 5 + 6.

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 get name of function's actual parameters in Python?

For example:
def func(a):
# how to get the name "x"
x = 1
func(x)
If I use inspect module I can get the stack frame object:
import inspect
def func(a):
print inspect.stack()
out:
[
(<frame object at 0x7fb973c7a988>, 'ts.py', 9, 'func', [' stack = inspect.stack()\n'], 0)
(<frame object at 0x7fb973d74c20>, 'ts.py', 18, '<module>', ['func(x)\n'], 0)
]
or use inspect.currentframe() I can get the current frame.
But I can only get the name "a" by inspect the function object.
Use inspect.stack I can get the call stack:"['func(x)\n']",
How can I get the name of actual parameters("x" here) when call the func by parsing the "['func(x)\n']"?
If I call func(x) then I get "x"
if I call func(y) then I get "y"
Edit:
A example:
def func(a):
# Add 1 to acttual parameter
...
x = 1
func(x)
print x # x expected to 2
y = 2
func(y)
print y # y expected to 3
Looking at your comment explanations, the reason you are trying to do this is:
Because I want to impelment Reference Parameters like in C++
You can't do that. In python, everything is passed by value, but that value is a reference to the object you pass. Now, if that object is mutable (like a list), you can modify it, but if it's immutable, a new object is created and set. In your code example, x is an int which is immutable, so if you want to change the value of x it, just return its new value from the function you are invoking:
def func(a):
b = 5 * a
# ... some math
return b
x = 1
x = func(x)
So what if you want to return multiple values? Use a tuple!
def func(a, b):
a, b = 5 * b, 6 * a
# ...
return b, a
a, b = 2, 3
a, b = func(a, b)
That is feasible, up to a point - but nonetheless,useless in any kind of "real code".
You can use it for backyard magic tricks:
Just retrieve the calling stack_frame like you are doing,
then loop linearly through the variables available in the calling frame for
one that references the same object you got as a parameter. The variables are in the
f_locals and f_globals dictionaries which are attributes of the frame object.
Of course, the parameter passed may not be in a variable name to start with:
it might be a constant in the caller, or it might be inside a dict or a list.
Either way, as #Nasser put it in the answer: it is not the way Python works. Objects exist in memory, and variable names, in each scope, just point to those objects. The names themselves are meaningless otherwise.
import inspect
from itertools import chain
def magic(x):
# note that in Python2, 'currentframe' could get a parameter
# to indicate which frame you want on the stack. Not so in Python3
fr = inspect.currentframe().f_back
for var_name, value in chain(fr.f_locals.items(), fr.f_globals.items()):
if value is x:
print ("The variable name in the caller context is {}".format(var_name))
def caler():
y = "My constant"
magic(y)
use lists as references
def func(a):
a[0] = a[0] + 1
x = [1]
func(x)
print x[0] # x expected to 2
y = [2]
func(y)
print y[0] # y expected to 3
This is my final solution. Use ast to parse the function statement and get the args.:
import inspect
import re
import ast
def func(a,b,c):
stack = inspect.stack()
stack_pre = stack[1]
function_call_string = ';'.join( stack_pre[4] ).strip('\n')
ret = re.search(r'(%s[ ]*\(.*\))' % func.func_name, function_call_string)
striped_function_call_string = ret.group()
parser = ast.parse(striped_function_call_string)
frame = inspect.currentframe()
for arg in parser.body[0].value.args:
iter_frame = frame.f_back
while iter_frame:
if arg.id in iter_frame.f_locals:
iter_frame.f_locals[arg.id] += 1
break
iter_frame = iter_frame.f_back
x=1;y=2;z=3;func(x,y,z)
print x,y,z
Out:
2 3 4

Categories

Resources