For efficiency purposes, I don't want to use functools.partial
for this task (adds overhead when using the function). I want to redefine a function's copy defaults arguments, by name.
For the moment I have the following:
import types
# http://stackoverflow.com/questions/6527633/how-can-i-make-a-deepcopy-of-a-function-in-python
def copy_func(f, name=None, defaults=None):
return types.FunctionType(f.__code__, f.__globals__, name or f.__name__, defaults or f.__defaults__, f.__closure__)
def f(a, b, c):
return 'a = {}, b = {} and c = {}'.format(a,b,c)
i = copy_func(f, 'i', (4,5))
print(f(3,4,5))
print(i('should be a'))
Can we modify the copy_func
tool to be called like so:
i = copy_func(f, a = 4, b = 5)
ie, using the names of arguments from the original function.
Of course, if the arguments do not exists, I hope the function would yell at me;)
I believe you can do this:
import inspect
def copy_func(f, name=None, **defaults):
args = inspect.getfullargspec(f).args
arg_defaults = tuple(defaults.get(arg) for arg in args)
return types.FunctionType(f.__code__, f.__globals__, name or f.__name__, arg_defaults or f.__defaults__, f.__closure__)
Since we need to provide a tuple
( arg_defaults
) for the defaults to FunctionType
, we can generate this tuple
by iterating through f
's arguments and seeing which ones have a default provided in defaults
. We use defaults.get
instead of defaults[...]
because a lack of a default is given as None
to FunctionType
.
copy_func(f, 'i', a = 4, b = 5)()
results in:
'a = 4, b = 5 and c = None'
If you want the function to yell at you if defaults
contains an invalid argument, you'd need to add this explicit condition between the declarations of args
and arg_defaults
:
if not all(arg in args for arg in defaults.keys()):
raise ValueError("defaults contains invalid arguments")
A solution I have found is not the best one, but it is some solution.
import types
def copy_func(f, name=None, defaults=None):
return types.FunctionType(f.__code__, f.__globals__, name or f.__name__, defaults=None, f.__closure__)
def f(a, b, c):
return 'a = {}, b = {} and c = {}'.format(a,b,c)
i = copy_func(f, defaults=(4, 5, 0))
This solution will replace c as well, this is the bad part of it. I could not find a solution that would set default values for a
and b
without setting a default value for c
. Hope this solution is suitable as well.
PS To be honest, I would not use myself this kind of setting default values. Besides, I would not copy the function itself but would write a wrapper function for f
.
PPS I hope you find a better solution, it would be interesting to me as well.
What you are looking for is Called Method Overloading
. Unfortunately Python Does not Support Method Overloading. But you can implement it using many Logical ways.
Few Approaches of handling method overloading are as Bellow
def copy_func(f, name=None, defaults=None):
if name not None:
return types.FunctionType(f.__code__, f.__globals__, name or f.__name__,
defaultsor f.__defaults__, f.__closure__)
if defaults not None:
# add another logic for handling function with defaults as parameter
def copy_func(f, name=None, *defaults):
for x in defaults:
# handle extra parameters here
Although above two ways works properly, but in both above ways we are only handling parameters of function to implement method overloading. If you really want to define two functions and write different Logic for them without checking for arguments based condition. You should use multipledispatch
package of pip.
from multipledispatch import dispatch
@dispatch(str,None,None)
def copy_func(f, name=None, defaults=None):
return types.FunctionType(f.__code__, f.__globals__, name or f.__name__, defaults
or f.__defaults__, f.__closure__)
@dispatch(str,str,str)
def f(a, b, c):
return 'a = {}, b = {} and c = {}'.format(a,b,c)
The only problem with this method is that you have to install third party package, but it allows you to actually use Method Overloading in Python and it saves you from writing tones of if condition and parsing **kwargs
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.