获取函数参数的默认值?
- 2025-02-27 09:06:00
- admin 原创
- 61
问题描述:
对于此功能
def eat_dog(name, should_digest=True):
print "ate dog named %s. Digested, too? %" % (name, str(should_digest))
我想在函数外部读取其参数和任何附加的默认值。因此,对于这个特定示例,我想知道name
没有默认值(即它是一个必需参数),并且这True
是的默认值should_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 公开模块中的函数。如果调用者省略了某些函数参数,我希望返回一个特定错误,该错误会指明省略的特定函数参数。如果客户端省略了参数,但函数签名中提供了默认值,我希望使用该默认值。
解决方案 1:
Python3.x
在 python3.x 世界中,您可能应该使用一个Signature
对象:
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(旧答案)
args/defaults 可以组合为:
import inspect
a = inspect.getargspec(eat_dog)
zip(a.args[-len(a.defaults):],a.defaults)
这里a.args[-len(a.defaults):]
是具有默认值的参数,显然a.defaults
是相应的默认值。
您甚至可以将输出传递zip
给dict
构造函数并创建适合关键字解包的映射。
查看文档,此解决方案仅适用于 python2.6 或更高版本,因为我假设inspect.getargspec
返回一个命名元组。早期版本返回一个常规元组,但相应地进行修改非常容易。以下是适用于旧版本(和新版本)的版本:
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))
想想看:
return dict(zip(reversed(args), reversed(defaults)))
也会起作用,并且对某些人来说可能更直观。
解决方案 2:
根据您的确切需要,您可能不需要该inspect
模块,因为您可以检查__defaults__
该函数的属性:
>>> 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
解决方案 3:
对于那些寻找一个版本来通过mgilson 的答案获取特定默认参数的人来说。
value = signature(my_func).parameters['param_name'].default
这是使用 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
解决方案 4:
您可以使用inspect
模块及其getargspec
功能:
inspect.getargspec(func)
获取Python 函数参数的名称和默认值。
tuple
返回以下四种情况之一:(args, varargs, keywords, defaults)
。args
是参数名称的列表(可能包含嵌套列表)。varargs
和是和参数keywords
的名称,或者。是默认参数值的元组,或者如果没有默认参数;如果这个元组有元素,它们对应于中列出的最后元素。*
`**None`**`defaults`**`None
nn
args`
有关如何检索参数名称及其默认值的确切代码,请参阅mgilson 的回答。
解决方案 5:
TLDR;
您可以通过其他帖子中提到的一些__dunder__
变量获得此值而无需任何导入。将其放入一个简单的辅助函数中可以得到一个默认值字典。
def get_defaults(fn):
"""
Get the default values of the passed function or method.
"""
output = {}
if fn.__defaults__ is not None:
# Get the names of all provided default values for args
default_varnames = list(fn.__code__.co_varnames)[:fn.__code__.co_argcount][-len(fn.__defaults__):]
# Update the output dictionary with the default values
output.update(dict(zip(default_varnames, fn.__defaults__)))
if fn.__kwdefaults__ is not None:
# Update the output dictionary with the keyword default values
output.update(fn.__kwdefaults__)
return output
利用以下 dunder 变量:
.__code__.co_varnames
:所有输入变量名称的元组.__code__.co_argcount
:函数中参数(非关键字)的数量.__defaults__
:默认值的元组.__kwdefaults__
:关键字默认值的字典
编辑 1 - 感谢@griloHBG - 添加了 if 语句以防止在未指定默认值时出现异常。
编辑 2 - 修改以使其更通用(支持函数和方法的默认值和 kwdefaults)
测试
功能测试
# Tests
def test_fn(a, b, c=3, d=4, *args, **kwargs):
pass
def test_fn_2(a, b, c=1, *args, d, e=2, **kwargs):
pass
print(get_defaults(test_fn)) #=> {'c': 3, 'd': 4}
print(get_defaults(test_fn_2)) #=> {'c': 1, 'e': 2}
类测试(方法 / 类方法 / 静态方法)
# Class Tests
class Test:
def __init__(self, a, b, c=3, d=4, *args, **kwargs):
pass
def test_fn_2(self, a, b, c=1, *args, d, e=2, **kwargs):
pass
@staticmethod
def test_fn_3(a, b, c=1, *args, d, e=2, **kwargs):
pass
@classmethod
def test_fn_4(cls, a, b, c=1, *args, d, e=2, **kwargs):
pass
# Uninitialized Class Tests
print(get_defaults(Test.__init__)) #=> {'c': 3, 'd': 4}
print(get_defaults(Test.test_fn_2)) #=> {'c': 1, 'e': 2}
print(get_defaults(Test.test_fn_3)) #=> {'c': 1, 'e': 2}
print(get_defaults(Test.test_fn_4)) #=> {'c': 1, 'e': 2}
# Initialized Class Tests
test_object = Test(1, 2)
print(get_defaults(test_object.__init__)) #=> {'c': 3, 'd': 4}
print(get_defaults(test_object.test_fn_2)) #=> {'c': 1, 'e': 2}
print(get_defaults(Test.test_fn_3)) #=> {'c': 1, 'e': 2}
print(get_defaults(Test.test_fn_4)) #=> {'c': 1, 'e': 2}
解决方案 6:
处理仅限关键字的参数(并且因为默认值和 kwonlydefaults 可以是None
):
spec = inspect.getfullargspec(func)
defaults = dict(zip(spec.args[::-1], (spec.defaults or ())[::-1]))
defaults.update(spec.kwonlydefaults or {})
解决方案 7:
在 Python 中,所有具有默认值的参数都位于没有默认值的参数之后。因此映射应从末尾开始,直到用尽默认值列表。因此逻辑如下:
dict(zip(reversed(args), reversed(defaults)))
给出正确映射的默认值。
解决方案 8:
对@conmak 的答案进行简单修改,None
当函数具有命名参数但没有默认值时,仍然返回带有值的字典:
def my_fn(a, b=2, c='a'):
pass
def get_defaults(fn):
### No arguments
if isinstance(fn.__code__.co_varnames, type(None)):
return {}
### no defaults
if isinstance(fn.__defaults__, type(None)):
return dict(zip(
fn.__code__.co_varnames,
[None]*(fn.__code__.co_argcount)
))
### every other case
return dict(zip(
fn.__code__.co_varnames,
[None]*(fn.__code__.co_argcount - len(fn.__defaults__)) + list(fn.__defaults__)
))
print(get_defaults(my_fn))
现在应该给出:
{'a': None, 'b': 2, 'c': 'a'}
扫码咨询,免费领取项目管理大礼包!