110

Given the following function:

def foo(a, b, c):
    pass

How would one obtain a list/tuple/dict/etc of the arguments passed in, without having to build the structure myself?

Specifically, I'm looking for Python's version of JavaScript's arguments keyword or PHP's func_get_args() method.

What I'm not looking for is a solution using *args or **kwargs; I need to specify the argument names in the function definition (to ensure they're being passed in) but within the function I want to work with them in a list- or dict-style structure.

Phillip B Oldham
  • 18,807
  • 20
  • 94
  • 134
  • 1
    Please Do Not Do This. The folks that maintain your code will be absolutely baffled by creating a useless dictionary out of perfectly good keyword arguments. They will be forced to rewrite to remove the dictionary. – S.Lott Mar 26 '10 at 10:11
  • Possible duplicate of [Getting method parameter names in python](http://stackoverflow.com/questions/218616/getting-method-parameter-names-in-python) – blahdiblah Jun 13 '13 at 19:00
  • 2
    @blahdiblah This question is asking how to obtain the parameters themselves, as opposed to the names of the parameters. I don't think it's a duplicate. – Anderson Green Jul 18 '13 at 22:21
  • 17
    @S.Lott: That's a rather narrow-minded view. Sometimes one wants to build a dictionary inside a function, initialised with some compulsory parameters. The function argument list makes it clear what those are, and believe me, they're not necessarily 'useless'. Please refer to Python's DRY principle, and don't be like some junior devs I've worked with, who whinge 'why would anybody want to do THAT?' when I suggest their API could be more versatile. – Michael Scheper Mar 22 '16 at 16:32

8 Answers8

127

You can use locals() to get a dict of the local variables in your function, like this:

def foo(a, b, c):
    print locals()

>>> foo(1, 2, 3)
{'a': 1, 'c': 3, 'b': 2}

This is a bit hackish, however, as locals() returns all variables in the local scope, not only the arguments passed to the function, so if you don't call it at the very top of the function the result might contain more information than you want:

def foo(a, b, c):
    x = 4
    y = 5
    print locals()

>>> foo(1, 2, 3)
{'y': 5, 'x': 4, 'c': 3, 'b': 2, 'a': 1}

I would rather construct a dict or list of the variables you need at the top of your function, as suggested in the other answers. It's more explicit and communicates the intent of your code in a more clear way, IMHO.

Pär Wieslander
  • 28,374
  • 7
  • 55
  • 54
  • 13
    Thanks. For anyone else looking to use this method, bear in mind that you should use `locals()` directly after the function definition, otherwise it will contain all variables defined within the function. – Phillip B Oldham Mar 26 '10 at 08:55
  • 5
    It's interesting to note that `locals()` returns a snapshot of local variables, not a pointer to some built-in dictionary. So if variables are defined later or a parameter is changed within the function, whatever `locals()` was assigned to won't change. This is what you want. (I Python.) – Michael Scheper Mar 22 '16 at 16:38
  • 5
    Also interesting to note that if you use locals() in a method, one of the keys in the dict is 'self'. So, make sure you correct / account for this. – Ben Sep 23 '16 at 03:23
  • how to handle below case? `def foo(*args, **kwargs)` how to get the dict of *args? – Dust break Apr 21 '22 at 14:03
13

Here's a function you can call to get the kwargs of the current function. Or if you want to use those lines directly in your own function instead of calling get_kwargs() just remove the .f_back

import inspect


def get_kwargs():
    frame = inspect.currentframe().f_back
    keys, _, _, values = inspect.getargvalues(frame)
    kwargs = {}
    for key in keys:
        if key != 'self':
            kwargs[key] = values[key]
    return kwargs


def test(x, y=100):
    z = 7
    print(get_kwargs())


test(5, 6)
# Output: {'x': 5, 'y': 6}
test(5)
# Output: {'x': 5, 'y': 100}
pyjamas
  • 4,608
  • 5
  • 38
  • 70
  • 1
    Also works if keyword args are used instead of positional args. – Eric Smith Apr 15 '21 at 21:57
  • I thought this was the obvious right answer because it is explicit, but in looking at the documentation, `inspect.getargvalues` 4th argument returns `locals()` of the current frame. So effectively, this calls a function that gets the frame of its calling environment to return `locals()` -- seems a bit circuitous. On the other hand, it allows you do other things in the function, like exclude some arguments. Thanks for the submission! – John Haberstroh Mar 16 '23 at 01:57
7

You can use the inspect module:

def foo(x):
    return x

inspect.getargspec(foo)
Out[23]: ArgSpec(args=['x'], varargs=None, keywords=None, defaults=None)

This is a duplicate of this and this.

Community
  • 1
  • 1
kroger
  • 448
  • 3
  • 10
6

I would use *args or **kwargs and throw an exception if the arguments are not as expected

If you want to have the same errors than the ones checked by python you can do something like

def check_arguments(function_name,args,arg_names):
    missing_count = len(arg_names) - len(args)
    if missing_count > 0:
        if missing_count == 1:
            raise TypeError(function_name+"() missing 1 required positionnal argument: "+repr(arg_names[-1]))
        else:
            raise TypeError(function_name+"() missing "+str(missing_count)+" required positionnal argument: "+", ".join([repr(name) for name in arg_names][-missing_count:-1])+ " and "+repr(arg_names[-1]))

using with somethin like

def f(*args):
    check_arguments("f",args,["a","b","c"])
    #whatever you want
    ...
Xavier Combelle
  • 10,968
  • 5
  • 28
  • 52
  • 2
    That would require lots of testing within the method body, while defining the parameters as part of the function would raise similar errors without any additional tests. – Phillip B Oldham Mar 26 '10 at 08:46
4

From the accepted answer from a duplicate (older??) question https://stackoverflow.com/a/582206/1136458 :

    frame = inspect.currentframe()
    args, _, _, values = inspect.getargvalues(frame)
Community
  • 1
  • 1
lib
  • 2,918
  • 3
  • 27
  • 53
0
class my_class():
        compulsory_default_kwargs = {'p':20,'q':30,'r':40}
        formal_args_names = ['var1','var2']
        def __init__(self,*args,**kwargs):
            for key , value in locals().items():
                if((key == 'args')):
                    if(len(args) == len(my_class.formal_args_names)):
                        for key_name , arg_value in zip(my_class.formal_args_names,args):
                            setattr(self,key_name,arg_value)
                    else:
                        raise Exception ('Error - Number of formal arguments passed mismatched required {} whereas passed {}'.format(len(my_class.formal_args_names),len(args)))

                elif((key == 'kwargs') & (len(kwargs)!=0)):
                    for kw_key , kw_value in kwargs.items():
                        if kw_key in my_class.compulsory_default_kwargs.keys():
                            setattr(self,kw_key,kw_value)
                        else:
                            raise Exception ('Invalid key-word argument passed {}'.format(kw_key))
                #elif((key!='self') & (key!='kwargs') ):
                   # setattr(self,key,value)
            for key , value in my_class.compulsory_default_kwargs.items():
                if key not in kwargs.keys():
                    setattr(self,key,value)


    this_value = 'Foo'    
    my_cl = my_class(3,this_value,p='B',r=10)
    my_cl.__dict__
-2

You've specified the parameters in the header?

Why don't you simply use that same info in the body?

def foo(a, b, c):
   params = [a, b, c]

What have I missed?

Oddthinking
  • 24,359
  • 19
  • 83
  • 121
  • 6
    This isn't analogous to JavaScript's `arguments` or PHP's `func_get_args()`, which is what I'm looking for. – Phillip B Oldham Mar 26 '10 at 08:41
  • I understand that. What is missing is *why* you would want a function like that. What is wrong with using the information you already have? – Oddthinking Mar 26 '10 at 09:04
  • 1
    He might want to abstract it for one, so he writes a helper that works with ALL functions, not having to write each of them params manually all the time. E.g. log_methods_arguments(arguments) – Hejazzman Jan 20 '15 at 21:53
  • @Nikos: You `log_methods_arguments` example could take `*args, **kwargs` parameters. The OP says the exact params must be specified. The motivation for this code smell is unclear. – Oddthinking Jan 21 '15 at 00:34
  • 1
    log_methods_arguments taking *args, **kwargs wouldn't change anything. You still need to get the arguments list from the method that you want to log, the one that calls log_method_arguments. – Hejazzman Jan 22 '15 at 01:00
  • And specifying exact params is not some "code smell"! That's rather a best practice... *args, **kwargs should only be used when there's a specific need for them... – Hejazzman Jan 22 '15 at 01:01
  • We seem to be at cross-purposes. If you want to write a generic log function, you can, with *args. If you want to specify the params of a function, you can. If you want to create a dictionary of the specified params, you can (like this answer shows). These appear to cover all the required situations. What is the problem that needs to be solved that requires a more complex scenario? – Oddthinking Jan 22 '15 at 01:32
-2

You can create a list out of them using:

args = [a, b, c]

You can easily create a tuple out of them using:

args = (a, b, c)
Michael Aaron Safyan
  • 93,612
  • 16
  • 138
  • 200