4

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?

bagrat
  • 7,158
  • 6
  • 29
  • 47

3 Answers3

3

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}
bagrat
  • 7,158
  • 6
  • 29
  • 47
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
  • Thanks for the answer. I have updated my question. 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. – bagrat Jul 19 '15 at 15:39
  • You can use `locals()` , in such case. – Anand S Kumar Jul 19 '15 at 15:47
  • in `a` I can make a call to `d`, and in `d` I can get `a` and it's `locals` using `inspect`. Does it make sense to additionally verify the `locals` using `inspect.getargspec`? – bagrat Jul 19 '15 at 15:51
  • You do not need that, as the dictionary `kwargs` in d printed, you only got the arguments from `a` as it is in a dictionary `kwargs` in `d` . – Anand S Kumar Jul 19 '15 at 15:52
  • You can iterate over `kwargs` dictionary to get the arguments in it. – Anand S Kumar Jul 19 '15 at 15:53
  • I just worried about that, as while debugging the code with `PyCharm` and doing `Watches`, it seems like defines some variables which get into `locals()`, anyway prefixed with `__`. – bagrat Jul 19 '15 at 15:55
  • Anyway thanks a lot for the answer, I will use some validation just in case. – bagrat Jul 19 '15 at 15:56
  • hmm, you called `locals()` at the very start of the function right? – Anand S Kumar Jul 19 '15 at 15:59
  • Exactly. That's interesting. When I set a breakpoint at the very first line of the function code, then add `locals()` to the `PyCharm` `Watches`, I get additional variable in the result, like `__py_debug_temp_var_XXX`, which is the instance of `locals()` dictionary inside the `Watches` :) – bagrat Jul 19 '15 at 16:07
  • I have added an [answer](http://stackoverflow.com/a/31503259/3264192) on how I finally solved my problem based on your answer. Thanks again :) – bagrat Jul 19 '15 at 16:17
1

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'}
>>> 
Mazdak
  • 105,000
  • 18
  • 159
  • 188
  • You are right about `locals()`, but it is ok to call `locals()` right at the beginning of the function, in that case I suppose it will only contain arguments, I am right? As for defining `f1` with `*args`, I do not want to do that, for the sake of code completion. – bagrat Jul 19 '15 at 15:41
  • Thanks a lot @Kasra, but [Anand S Kumar's answer](http://stackoverflow.com/a/31502848/3264192) works better for me. – bagrat Jul 19 '15 at 15:56
1

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}
Community
  • 1
  • 1
bagrat
  • 7,158
  • 6
  • 29
  • 47