Frage

I have a function which takes a lot of parameters, and since I dont want to remember their positions I decided to use named arguments

def f(a=None, b=None, c=None):
    print a,b,c
f('test', c=5, b='second param')
>>> test second param 5

now, usually I only change one parameter at a time, so i want to call the function by only typing f(c=3.14) with the intended result being f(a=a, b=b, c=3.14), namely every argument that is not explicitely passed should be read from the local scope.

But of course it doesnt work since to use named arguments I have to set a default value, if I use **kwargs instead, it simply ignores the arguments

def f(**kwargs):
    print a,b,c
a=1; b=2; c=3
f(a=2, b=4, c=8)
>>> 1 2 3 # instead of 2 4 8

I used to define a new function for each parameter but I dont like this approach although it has been the most effective so far

def fc(c_new):
    return f(a, b, c_new)

How do I make the function use the variables in its current scope as default values for its named arguments?

War es hilfreich?

Lösung

Here is a solution with a decorator:

from functools import wraps
from inspect import getcallargs


def defaults_from_globals(f):
    @wraps(f)
    def new(**kwargs):
        # filter only those vars, that are in the list of function's named args
        from_globals = {arg: globals()[arg] for arg in getcallargs(f)}
        # overwrite them with user supplied kwargs
        from_globals.update(kwargs)
        return f(**from_gobals)

    return new


@defaults_from_globals
def f(a=None, b=None, c=None):
    return a, b, c


a = 1
b = 2
c = 3

print f(a=2, b=4) # 2 4 3

Andere Tipps

For the sake of completeness here is one more solution I just found, it's more of a dirty hack, because the use of exec is frowned upon in most cases* but it's more straightforward to understand.

*) in this case the user has control over the code and it is not in his interest to intentionally screw up his own work

def f(x=None, y=None, z=None):
    for  key,val in locals().items():
        if val==None: exec("%s = globals()[key]"%key)
    print x,y,z

i think you should use decorators

import functools

_last_args = {}
def foo(f):
    @functools.wraps(f)
    def wrapper( *args, **kwargs):
        _last_args.update(kwargs)
        ret =  f(*args, **_last_args)
        return ret
    return wrapper

@foo
def bar(a=None,b=None,c=None):
    print(a,b,c)

bar(a=1,b=2,c=3)
bar(a=10,b=20)
bar(a=100)

prints:

1 2 3
10 20 3
100 20 3
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top