I'd like to know how I would go about doing something like this (in python 2):
def myFunction(fn, params):
return ("You called " + fn.__name__ + "(" + str(params) ")" +
" and got " + str(fn(params)))
Say I have a couple functions:
def doSomething(s):
# manipulation
return s
def doAnotherThing(someInt, someOtherInt, someString):
# do something with them
return someValue
I'd like to be able to call myFunction(doSomething, "hello"), and get You called doSomething("hello") and got "something". This approach seems to work for functions that take a single input, but I can't get it to work for functions that take multiple inputs, e.g. calling myFunction(doAnotherThing, (myInt, myOtherInt, myString)). I think I need to do something involving * and ** so that I can take the keywords arbitrarily instead of as a tuple, but I'm not quite sure how to go about doing that.
You're close, all you need to do is add a * in front of the tuple (or ** in front of a dict for keyword arguments) when calling the interior function. This is called argument unpacking.
def wrapper(fn, params, keyword_params):
return fn(*params, **keyword_params)
def myfunc(a, b, c=0):
return (a + b)/c
wrapper(myfunc, (2, 5), {'c': 3})
You can also use arbitrary argument lists to potentially simplify the wrapper function. This will allow you to automatically package additional arguments to feed to interior functions without having to pre-package them in tuples and dictionaries.
def wrapper(fn, *params, **keyword_params): # Note the asterisks
return fn(*params, **keyword_params)
def myfunc(a, b, c=1):
return (a + b)/c
wrapper(myfunc, 2, 5, c=3) # Nicer function call
Note that just using the first method will actually break single-argument implementations, as the * operator expects an iterator to unpack. So you have to either always pre-package the arguments or add some type-checking in the wrapper function.
def wrapper(fn, params):
return fn(*params)
def myfunc(a):
return 2*a
wrapper(myfunc, 2)
# TypeError: myfunc() argument after * must be a sequence, not int
wrapper(myfunc, (2,)) # Package the argument in a single-element tuple
# 4
The second method doesn't have this problem.
Here is one way:
def myFunction(fn, *params):
return ("You called " + fn.__name__ + str(params) +
" and got " + str(fn(*params)))
import math
print myFunction(math.sqrt, 4)
print myFunction(open, '/etc/passwd', 'r')
print myFunction(lambda x: x+1, 41)
Result:
You called sqrt(4,) and got 2.0
You called open('/etc/passwd', 'r') and got <open file '/etc/passwd', mode 'r' at 0x7f20e9cb65d0>
You called <lambda>(41,) and got 42
Related
Mock version of the problem
For a function
def f(a,b,c):
return a+b+c
The function
def fix(func, **kwargs):
fa = kwargs.get('a')
fb = kwargs.get('b')
if fa is not None and fb is not None:
def f(*args):
return func(a=fa, b=fb, c=args[0])
elif fa is not None:
def f(*args):
return func(a=fa, b=args[0], c=args[1])
elif fb is not None:
def f(*args):
return func(a=args[0],b=fb, c=args[1])
else:
def f(*args):
return func(args)
return f
allows to obtain a new function by fixing some of the parameters of func.
For example: fix(g, b=3) would give us a function like
def fixed_b_in_g(a,c):
return g(a,3,c)
Question: I would like to see if there is some trick to use fix in such a way that produces a function like
def fix_a_equal_b_in_g(a,c):
return g(a,a,c)
Concrete problem
The function scipy.stats.rv_continuous.fit allows to fit parameters of a distribution to an input sample. It allows to input some keyword arguments (like fix above does) to tell it to keep some of the parameters fixed to values that the user inputs. Internally scipy.stats.rv_continuous.fit has a function, scipy.stats.rv_continuous._reduce_func, that does more or less what dix does (better implemented than my fix for example).
In my case, rather than fixing some parameters to values, I would like to fit to keep two parameters (say a and b) equal to each other, but still free during the fitting.
We can use this function to copy a keyword argument whose name is base_kwarg_name to added_kwarg_name:
def with_copied_kwargs(func, added_kwarg_names_by_base):
def fixed_func(*args, **base_kwargs):
added_kwargs = {
added_kwarg_name: base_kwargs[base_kwarg_name]
for base_kwarg_name, added_kwarg_name in added_kwarg_names_by_base.items()
}
return func(*args, **base_kwargs, **added_kwargs)
return fixed_func
Given:
def add(*, a, b, c):
return a + b + c
then modified_add = with_copied_kwargs(add, {"b": "c"}) is equivalent to:
def modified_add(*, a, b):
return add(a=a, b=b, c=b)
with_copied_kwargs can then be used along with functools.partial to both both copy keyword arguments and provide values incrementally. modified_add = functools.partial(with_copied_kwargs(add, {"b": "c"}), a=1) is equivalent to:
def modified_add(*, b):
return add(a=1, b=b, c=b)
Note that I add * (see PEP 3102) before all parameters in functions I then apply with_copied_kwargs to because the minute people start using positional arguments, things would get messy. So better to restrict it to keyword-only arguments.
Imagine there are three functions, all them accept and return the same type args.
Normally, we can write it as fun3(fun2(fun1(args)), this can be say that a sequence function act on parameter in order, which likes one variety Higher-order functions "map".
You know in Mathematica, we can write this as fun3#fun2#fun1#args.
Now the question is that can we integrate fun3#fun2#fun1 as another fun without modifying their definition, so fun(args) can replace fun3(fun2(fun1(args)), this looks more elegant and concise.
def merge_steps(*fun_list):
def fun(arg):
result = arg
for f in fun_list:
result = f(result)
return result
return fun
def plus_one(arg):
return arg + 1
def double_it(arg):
return arg ** 2
def power_ten(arg):
return arg ** 10
combine1 = merge_steps(power_ten, plus_one, double_it)
combine2 = merge_steps(plus_one, power_ten, double_it)
combine1(3)
> 3486902500
or use lambda:
steps = [power_ten, plus_one, double_it]
reduce(lambda a, f: f(a), steps, 3)
> 3486902500
I think you can use Function Recursion in python to do this.
def function(args, times):
print(f"{times} Times - {args}")
if times > 0 :
function(args,times - 1)
function("test", 2)
Note: I just add times argument to not generate infinite loop.
I'm not certain I understand your question, but are you talking about function composition along these lines?
# Some single-argument functions to experiment with.
def double(x):
return 2 * x
def reciprocal(x):
return 1 / x
# Returns a new function that will execute multiple single-argument functions in order.
def compose(*funcs):
def g(x):
for f in funcs:
x = f(x)
return x
return g
# Demo.
double_recip_abs = compose(double, reciprocal, abs)
print(double_recip_abs(-2)) # 0.25
print(double_recip_abs(.1)) # 5.0
I want to ask if there is a way to prevent unnecessary duplicate of code when passing the same arguments into a function's optional arguments.
Hopefully the following example provides a good idea of what I am trying to do:
def f(arg1):
def g(optional_1=0, optional_2=0, optional_3=0):
return arg1+optional_1+optional_2+optional_3
return g
b, c = 2, 3
f1 = f(1)
f2 = f(2)
calc_f1 = f1(optional_2=b, optional_3=c)
calc_f2 = f2(optional_2=b, optional_3=c)
As you can see, f1 and f2 only differ in the arg1 passed into f and afterwards I call them with the same variables for the same optional arguments.
It is fine when the code is short, but when I have over 10 optional arguments, it becomes unnecessarily long and redundant.
Is it possible to do something like
optional_variable_pair = #some way to combine them
calc_f1 = f1(optional_variable_pair)
calc_f2 = f2(optional_variable_pair)
so I get a more succinct and easy to read code?
Any function with multiple optional arguments is a bit smelly because:
you get so many argument combinations that it requires a large amount of testing.
because of all the options the function has to have alot of conditionals and routes which increase its cyclomatic complexity.
You can apply a refactoring to extract the whole argument list into an Object and have the function work on that object. This works really well if you can find a unifying name that describes your argument list and fits whatever metaphor you are using around the function. You can even invert the call so that the function becomes a method of the Object, so you get some encapsulation.
To answer the question you asked, the answer is yes. You can do almost exactly what you want using keyword argument unpacking.
def f(arg1):
def g(optional_1=0, optional_2=0, optional_3=0):
return arg1+optional_1+optional_2+optional_3
return g
optional_variable_pair = {
'optional_2': 2,
'optional_3': 3
}
f1 = f(1)
f2 = f(2)
calc_f1 = f1(**optional_variable_pair)
calc_f2 = f2(**optional_variable_pair)
If I'm reading your intent correctly, though, the essence of your question is wanting to pass new first arguments with the same successive arguments to a function. Depending on your use case, the wrapper function g may be unnecessary.
def f(arg1, *, optional_1=0, optional_2=0, optional_3=0):
return optional_1 + optional_2+optional_3
optional_variable_pair = {
'optional_2': 2,
'optional_3': 3
}
calc_f1 = f(1, **optional_variable_pair)
calc_f2 = f(2, **optional_variable_pair)
Obviously, if the first argument continues incrementing by one, a for loop is in order. Obviously, if you are never using the optional_1 parameter, you do not need to include it. But, moreover, if you find yourself using numbered arguments, there is a good chance you really should be working with tuple unpacking instead of keyword unpacking:
def f(*args):
return sum(args)
optional_variable_pair = (2, 3)
for i in range(1, 3):
calc = f(i, *optional_variable_pair)
# ...do something with calc...
You may also be interested in researching functools.partial, as well, which can take the place of your wrapper function g, and allow this:
import functools
def f(*args):
return sum(args)
f1 = functools.partial(f, 1)
f2 = functools.partial(f, 2)
calc_f1 = f1(2, 3) # = 1 + 2 + 3 = 6
calc_f2 = f2(2, 3) # = 2 + 2 + 3 = 7
You use key-value pairs as function argsuments, for this purpose you can use *args and **kwargs:
optional_variable_pair = {
"optional_1": 1,
"optional_2": 2,
"optional_3": 3,
}
calc_f1 = f1(**optional_variable_pair)
calc_f2 = f2(**optional_variable_pair)
Just for reference my code
def makeList (start,end,h):
a=[]
a.append(start)
n=(end-start)/h
while(n!=0):
new=a[-1] +h
a.append(a[-1] +h)
n=n-1
return a
def generateBase(xList,f):
if(len(xList)==0):
return []
elif(xList[0]==0 or len(xList)==1):
return ([(xList[0],0,0)] + generateBase(xList[1:],f))
else:
return ([(xList[0],0,(f(xList[0])))] + generateBase(xList[1:],f))
def getThird(a):
return a[2]
def fdm(alpha,startx,endx,startt,endt,dx,dt,f):
baseSolution=generateBase((makeList(startx,endx,dx)),f)
totalSoltuion= baseSolution + startCalc(alpha,(makeList(startx,endx,dx)),(makeList(startt,endt,dt))[1:],baseSolution,dx,dt,[],[])
def startCalc(alpha,xList,tList,phiSolutions,dx,dt,newPhi,newX):
print newPhi
if(len(tList)==0):
return []
elif (len(xList)==1):
return ([(xList[0],tList[0],0)] + startCalc(alpha,(newX + [xList[0]]),tList[1:],(newPhi + [(xList[0],tList[0],0) ]),dx,dt,[],[]))
elif (xList[0]==0):
return ([(xList[0],tList[0],0)] + startCalc(alpha,(xList[1:]),tList,phiSolutions,dx,dt,(newPhi + [(xList[0],tList[0],0)]),(newX + [xList[0]])))
else:
print getThird(phiSolutions[0])
print getThird(phiSolutions[1])
print getThird(phiSolutions[2])
sol=newPhi(xList[0],tList[0],getThird(phiSolutions[0]),getThird(phiSolutions[1]),getThird(phiSolutions[2]),alpha)
return ([sol] + startCalc(alpha,(xList[1:]),tList,phiSolutions[1:],dx,dt,(newPhi + [sol]),(newX + [xList[0]])))
def newPhi(x,t,phiL,phiC,phiR,dx,dt,alpha):
return (x,t,(phiC + (alpha*(dt/(dx**2)))*(phiR-(2*phiC)+phiL)) )
def showMe(SolutionList):
GraphSolution(SolutionList)
showMe(fdm(1,0,1,0,1,.1,.01,(lambda x:x+1)))
The issue is here
sol=newPhi(xList[0],tList[0],getThird(phiSolutions[0]),getThird(phiSolutions[1]),getThird(phiSolutions[2]),alpha)
I get this issue
TypeError: 'list' object is not callable
The things in these arrays are real numbers, i thought this issue only happens when i try to call a list like a function if i did like phiSolution(0) im not sure thought whats the issue, i print everything out and its a number, if someone could give me some insight that would be great.
Look at your recursive call:
return ([(xList[0],tList[0],0)] + startCalc(alpha,(newX + [xList[0]]),tList[1:],(newPhi + [(xList[0],tList[0],0) ]),dx,dt,[],[]))
In particular, look carefully at your argument list. It breaks down to these arguments:
startCalc(alpha, # arg 1
(newX + [xList[0]]), #arg 2
tList[1:], #arg 3
(newPhi + [(xList[0],tList[0],0) ]), # arg 4
dx, # arg 5
dt, # arg 6
[], # arg 7
[]) # arg 8
According to your definition of startCalc...
startCalc(alpha,xList,tList,phiSolutions,dx,dt,newPhi,newX)
... newPhi is argument #7 by position. Therefore, when you make your tail-recursive call, an empty list is being assigned to newPhi. So newPhi is a list, and sol=newPhi(<anything>) is indeed attempting to call it. If you're trying to index into the list, use brackets:
sol = newPhi[index]
Also, as mentioned in another answer, you're using the identifier newPhi both as a function name and an input argument name to startCalc. You need to change one of these to resolve the conflict.
Because newPhi is a list, and therefore is not callable.
Note that you have defined newPhi twice: Once as a function (at about line 48 in the code, between the definitions of startCalc and showMe), and once as the 7th parameter to the startCalc function at about line 29. The definition in force when the TypeError is raised is the one where it's a startCalc parameter.
startCalc is called by the second line of the fdm function at about line 27:
totalSoltuion= baseSolution + startCalc(alpha,(makeList(startx,endx,dx)),(makeList(startt,endt,dt))[1:],baseSolution,dx,dt,[],[])
And if you'll count 7 parameters in, you'll see that you're passing an empty list ([]).
If you want newPhi to be a function there, move the definition above the definition of startCalc, and rename the current newPhi parameter which is interfering with your newPhi function. You cannot have it be both a list (see lines 34 and 36) and a function (line 42).
I want to define a function that takes some arguments as input, and uses them to make another function, then outputs the new function.
For example:
makeIncrease(n) --> return a function that takes an argument, and return (argument + n)
applyIncrease(increaseFn, m) --> will apply increaseFn to argument m
So if I do this: applyIncrease(makeIncrease(n), m) --> will return m+n
How can I do it in python?
You can read about decorators in Python for more on this. For your specific question:
def applyIncrease(increaseFn, m):
return increaseFn(m)
def makeIncrease(n):
def _innerFn(arg):
return arg + n
return _innerFn
applyIncrease accepts a function and argument, and applies the function to the argument.
makeIncrease accepts an argument n.
Let's say n=2 for the sake of an example. makeIncrease(2) returns a function that takes an argument and adds 2 to it.
Although I began _innerFn with an underscore, this is only a convention - the underscore is not required for the decorator to work.
Note also that functions are first class objects in Python, and that makeIncrease returns _innerFn and not _innerFn(). Return functions exactly as you would variables or object references - no parentheses.
Here are your functions in the interpreter. Note that the object reference wrapped_function refers to _innerFn, i.e. the return value of makeIncrease(2)
>>> wrapped_function = makeIncrease(2)
>>> wrapped_function
<function _innerFn at 0x100496758>
>>> total = applyIncrease(wrapped_function, 3)
>>> total
5
class Example:
def result():
def nestedResult(a,b):
multiply = a*b
return multiply
return nestedResult
if __name__ == "__main__":
x = result()
print "multiplication_result:", x(1,10)