简体   繁体   中英

Redefine defaults arguments of a python function by name

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

None Check for Non necessary Parameters

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

Use keyword arguments

def copy_func(f, name=None, *defaults):
    for x in defaults:
        # handle extra parameters here

Use multipledispatch

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM