How to get the set of all arguments in Python?

7
Python has both positional and named parameters as * and ** operators that allow you to receive an arbitrary number of additional arguments:

def foo(a, b, c=1, *args, **kwargs):
    print(a, b, c, args, kwargs)

foo(1, 2)          # 1, 2, 1, (), {}
foo(1, 2, 3)       # 1, 2, 3, (), {}
foo(1, 2, 3, 4)    # 1, 2, 3, (4,), {}
foo(1, 2, 3, 4, 5) # 1, 2, 3, (4,5), {}

foo(b=1, a=2)                # 2, 1, 1, (), {}
foo(b=1, a=2, c=3)           # 2, 1, 3, (), {}
foo(b=1, a=2, c=3, d=4)      # 2, 1, 3, (), {'d':4}
foo(b=1, a=2, c=3, d=4, e=5) # 2, 1, 3, (), {'d':4, 'e':5}

foo(1, 2, 3, 4, 5, d=6, e=7) # 1 2 3 (4, 5) {'d': 6, 'e': 7}

I wonder if in a case like this, you mix explicitly declared parameters with arbitrary argument lists / sets, if you can get the set of all of them , not just the additional ones. Example:

def foo(a, b, c=1, *args, **kwargs):
    print(list_args(...))
    print(dict_args(...))

foo(1, 2, 3, 4, 5, d=6, e=7)
# (1, 2, 3, 4, 5, 6, 7)
# {'a':1, 'b':2, 'c':3, 3:4, 4:5, 'd':6, 'e':7}

(just one example, such a feature could have additional constraints - such as not mixing *args with **kwargs , or representing the arguments in a different way - but interestingly something like that existed / could be done)

Is it possible? Compare with the JavaScript language, which allows both named parameters and access to the list of all arguments via arguments :

function foo(a, b, c) {
    console.log(arguments);
}

foo(1, 2, 3, 4, 5); // [1, 2, 3, 4, 5]
                    // Repare que o conjunto inclui "a", "b" e "c", ao contrário do Python
Note: The motivation for this question is to find a way to create a function that has a well-defined set of parameters (with exact number and names, and maybe optional values) but can pass all of them (after some validation, or even change ) to another function that has exactly the same parameters.

    
asked by anonymous 24.11.2015 / 08:29

2 answers

2

You can do:

import inspect

def func(a, b, c):
    frame = inspect.currentframe() #equivalente a sys._getframe(0)
    args_names, _, _, locals_ = inspect.getargvalues(frame)
    args = [locals_[i] for i in args_names]
    kwargs = dict(zip(args_names, args))
    print "args: %r" %args
    print "kwargs: %r" %kwargs


>>> func(1, 2, 3)
args: [1, 2, 3]
kwargs: {'a': 1, 'c': 3, 'b': 2}

>>> func(c=1, a=2, b=3)
args: [2, 3, 1]
kwargs: {'a': 2, 'c': 1, 'b': 3}

You can do a function that dump the arguments:

import sys
import inspect

def dumpargs():
    frame = sys._getframe(1) # 0: funcao atual, 1: pega a funcao anterior na pilha
    args_names, _, _, locals_ = inspect.getargvalues(frame)
    args = [locals_[i] for i in args_names]
    kwargs = dict(zip(args_names, args))
    return args, kwargs


def outra(a, b, c, d=1):
    args, kwargs = dumpargs()
    print "kwargs: %r" %kwargs
    print "args: %r" %args

>>> outra(1, 2, 3)
kwargs: {'a': 1, 'c': 3, 'b': 2, 'd': 1}
args: [1, 2, 3, 1]

>>> outra(1, 2, 3, 4)
kwargs: {'a': 1, 'c': 3, 'b': 2, 'd': 4}
args: [1, 2, 3, 4]

>>> outra(b=1, c=2, d=3, a=4)
kwargs: {'a': 4, 'c': 2, 'b': 1, 'd': 3}
args: [4, 1, 2, 3]

Update 01

As noted by the question author, part of this code is only guaranteed to work in CPython. sys._getframe , for example, is specific to CPython there is no guarantee to work in PyPy , Jython, or another python implementation. Also, inspect.getargvalues is deprecated since python 3.5

Update 02

The code works in PyPy 2.6.0 (python 2.7.9) and Jython 2.7.0

    
24.11.2015 / 18:34
1

I'm not sure what you really want, however, I believe I have a simpler approach.

Did you try calling locals right at the beginning of the function?

def foo(a, b, c=1, *args, **kw):
    assinatura = locals()
    print(assinatura)


In [5]: foo(1, 2, 3, 4, 5, d=6, e=7)
{'kw': {'d': 6, 'e': 7}, 'args': (4, 5), 'a': 1, 'b': 2, 'c': 3}

If you need something more accurate, I suggest that you use the getfullargspec method of the inspect module. It will return all the information about the function parameters, which can be compared with the result of locals .

    
25.11.2015 / 08:55