bagrat
bagrat

Reputation: 7418

Python: put all function arguments into **kwargs automatically

Description

Say I have the following function, which makes a call to another function:

def f1(arg1, arg2, arg3):
    f2(...)

The arguments of f1 and f2 are the same, or f2 might look like this:

def f2(**kwargs)
    pass  # whatever

The client code is going to define and call f1, and it is required that the signature of f1 explicitly defines all arguments, and thus no **kwargs is allowed for f1.

So, to make a call to f2 from inside f1 I have to do this:

def f1(arg1, arg2, arg3):
    f2(arg1, arg2, arg3)

Question

Is there a way I can pass arguments to f2 without explicitly writing them? Ideally, I think it should look like this:

def f1(arg1, arg2, arg3):
        kwargs = <Some Magic Here>
        f2(**kwargs)

Any magic?

UPDATE

Possible Solution

Is there a way I can combine locals() and inspect.getargspec() to aggregate my **kwargs?

Upvotes: 4

Views: 2766

Answers (3)

Anand S Kumar
Anand S Kumar

Reputation: 90889

In General

Well, you can create kwargs as a dictionary of all the arguments that f2() accepts and pass it. Though I do not see any benefit from that, using -

def f1(arg1, arg2, arg3):
    f2(arg1=arg1, arg2=arg2, arg3=arg3)

Looks fine to me , and would be easier than building the dictionary and calling it as **kwargs.

Anyway the way to do it is -

>>> def a(a,b,c):
...     kwargs = {'a':a , 'b':b , 'c':c}
...     d(**kwargs)
...
>>> def d(**kwargs):
...     print(kwargs)
...
>>> a(1,2,3)
{'c': 3, 'a': 1, 'b': 2}

For your use case

The problem is that f1 is going to be defined by the client, the processing of argument is common for all, so I want to hide the processing details, so that the client passes all the arguments to the implementation. Furthermore, I want to ease the definition and automatically pass all arguments and not specify them explicitly.

locals() inside a function returns you the copy of the local variables in the function at that time as a dictionary. If as in your question if the definition of f1() and f2() are same you can use locals() , by calling it at the start of the function before any other code. Example -

>>> def a(a,b,c):
...     lcl = locals()
...     print(lcl)
...     d(**lcl)
...     e = 123
...     print(locals())
...
>>> def d(**kwargs):
...     print(kwargs)
...
>>> a(1,2,3)
{'c': 3, 'a': 1, 'b': 2}
{'c': 3, 'a': 1, 'b': 2}
{'c': 3, 'a': 1, 'e': 123, 'lcl': {...}, 'b': 2}

Upvotes: 3

bagrat
bagrat

Reputation: 7418

Here is how I solved my problem based on Anand S Kumar's answer as well as Alex Martelli's answer to another question:

def f2():
    kwargs = inspect.getouterframes(inspect.currentframe(), 2)[1][0].f_locals
    print(kwargs)

def f1(arg1, arg2, arg3)
    f2()  # this should be called right at the first line

>>> f1(1, 2, 3)
{'arg1': 1, 'arg2': 2, 'arg3': 3}

Upvotes: 1

Kasravnd
Kasravnd

Reputation: 107287

What you want here is passing the arguments of the parent function to an enclosing function.So fist I must say that you can not use local() because local is contain the local variables which is contain the argument and all local variables that you have define in your function buddy, but if you just want to get all the arguments when they are dynamic and like your example, I suggest to use *args in parents function :

def f1(*args):

But there is a point here, since you want to use **kwargs and it is for collecting the arbitrarily keyword arguments you need to initial names of your argument.

For example you can use a dict comprehension like following :

def f1(*args):
        kwargs = {'arg{}'.format(i):j for i,j in enumerate(args,1)}
        f2(**kwargs)

Also if you are sure that there is no local variable in your function buddy specially before defining the enclosing function you can use locals :

Example :

>>> globsl_a=8
>>> def fun(arg1,arg2,arg3):
...   print locals()
... 
>>> fun(5,[3,4],'text')
{'arg1': 5, 'arg2': [3, 4], 'arg3': 'text'}
>>> 

Upvotes: 1

Related Questions