得到函数参数的默认值?

这个功能

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

我想在函数外部读取它的参数和任何附加的默认值。因此,对于这个特定的示例,我希望知道 name没有默认值(即它是一个必需的参数) ,而 Trueshould_digest的默认值。

我知道 inspect.getargspec(),它确实给了我关于参数和默认值的信息,但是我看不出两者之间有什么联系:

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

从这个输出中,我如何知道 True(在 defaults元组中)是 should_digest的默认值?

此外,我知道“请求宽恕”模型是用来解决问题的,但不幸的是,这个错误的输出不会告诉我缺少的论点的名称:

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

为了给出上下文(我为什么要这样做) ,我将通过 JSON API 公开模块中的函数。如果调用方省略了某些函数参数,我希望返回一个特定的错误,该错误命名被省略的特定函数参数。如果客户机省略了一个参数,但在函数签名中提供了一个默认值,我希望使用该默认值。

36237 次浏览

You can use inspect module with its getargspec function:

inspect.getargspec(func)

Get the (args, varargs, keywords, defaults)3. A tuple of four things is returned: (args, varargs, keywords, defaults). (args, varargs, keywords, defaults)4 (it may contain nested lists). varargs and keywords are the names of the * and ** arguments or None. (args, varargs, keywords, defaults)5 or None if there are no default arguments; if this tuple has (args, varargs, keywords, defaults)0 elements, they correspond to the last (args, varargs, keywords, defaults)0 elements listed in args.

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

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.


In python, all the arguments with default value come after the arguments without default value. So the mapping should start from the end till you exhaust the default value list. Hence the logic:

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

gives the correctly mapped defaults.

to take care of keyword-only args (and because defaults and kwonlydefaults can be None):

spec = inspect.getfullargspec(func)
defaults = dict(zip(spec.args[::-1], (spec.defaults or ())[::-1]))
defaults.update(spec.kwonlydefaults or {})

Depending on exactly what you need, you might not need the inspect module since you can check the __defaults__ attribute of the function:

>>> eat_dog.__defaults__
(True,)
>>> eat_dog.__code__.co_argcount
2
>>> eat_dog.__code__.co_varnames
('name', 'should_digest')
>>>
>>> eat_dog.__kwdefaults__
>>> eat_dog.__code__.co_kwonlyargcount
0

To those looking for a version to grab a specific default parameter with mgilson's answer.

value = signature(my_func).parameters['param_name'].default

Here's a full working version, done in Python 3.8.2

from inspect import signature


def my_func(a, b, c, param_name='apple'):
pass


value = signature(my_func).parameters['param_name'].default


print(value == 'apple') # True

You can get this via some of the __dunder__ vars as mentioned by other posts. Putting that into a simple helper function can get you a dictionary of default values.

  • .__code__.co_varnames: A tuple of all input variables
  • .__defaults__: A tuple of the default values
    • It is worth noting that this tuple only incudes the default provided variables which must always be positioned last in the function arguments

You can use these two items to match the last n variables in the .__code__.co_varnames with all the items in the .__defaults__

EDIT Thanks to @griloHBG - Added if statement to prevent exceptions when no defaults are specified.

def my_fn(a, b=2, c='a'):
pass


def get_defaults(fn):
if fn.__defaults__==None:
return {}
return dict(zip(
fn.__code__.co_varnames[-len(fn.__defaults__):],
fn.__defaults__
))


print(get_defaults(my_fn))

Should give:

{'b': 2, 'c': 'a'}