Get a function argument's default value?

For this function

def eat_dog(name, should_digest=True):
    print "ate dog named %s. Digested, too? %" % (name, str(should_digest))

I want to, external to the function, read its arguments and any default values attached. So for this specific example, I want to know that name has no default value (i.e. that it is a required argument) and that True is the default value for should_digest.

I'm aware of inspect.getargspec(), which does give me information about arguments and default values, but I see no connection between the two:

ArgSpec(args=['name', 'should_digest'], varargs=None, keywords=None, defaults=(True,))

From this output how can I tell that True (in the defaults tuple) is the default value for should_digest?

Additionally, I'm aware of the "ask for forgiveness" model of approaching a problem, but unfortunately output from that error won't tell me the name of the missing argument:

>>> eat_dog()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: eat_dog() takes at least 1 argument (0 given)

To give context (why I want to do this), I'm exposing functions in a module over a JSON API. If the caller omits certain function arguments, I want to return a specific error that names the specific function argument that was omitted. If a client omits an argument, but there's a default provided in the function signature, I want to use that default.


Solution 1:

Python3.x

In a python3.x world, you should probably use a Signature object:

import inspect

def get_default_args(func):
    signature = inspect.signature(func)
    return {
        k: v.default
        for k, v in signature.parameters.items()
        if v.default is not inspect.Parameter.empty
    }

Python2.x (old answer)

The args/defaults can be combined as:

import inspect
a = inspect.getargspec(eat_dog)
zip(a.args[-len(a.defaults):],a.defaults)

Here a.args[-len(a.defaults):] are the arguments with defaults values and obviously a.defaults are the corresponding default values.

You could even pass the output of zip to the dict constructor and create a mapping suitable for keyword unpacking.


looking at the docs, this solution will only work on python2.6 or newer since I assume that inspect.getargspec returns a named tuple. Earlier versions returned a regular tuple, but it would be very easy to modify accordingly. Here's a version which works with older (and newer) versions:

import inspect
def get_default_args(func):
    """
    returns a dictionary of arg_name:default_values for the input function
    """
    args, varargs, keywords, defaults = inspect.getargspec(func)
    return dict(zip(args[-len(defaults):], defaults))

Come to think of it:

    return dict(zip(reversed(args), reversed(defaults)))

would also work and may be more intuitive to some people.


Solution 2:

You can use inspect module with its getargspec function:

inspect.getargspec(func)

Get the names and default values of a Python function’s arguments. A tuple of four things is returned: (args, varargs, keywords, defaults). args is a list of the argument names (it may contain nested lists). varargs and keywords are the names of the * and ** arguments or None. defaults is a tuple of default argument values or None if there are no default arguments; if this tuple has n elements, they correspond to the last n elements listed in args.

See mgilson's answer for exact code on how to retrieve argument names and their default values.