我试着研究这个问题,但找不到答案——对不起,如果我错过了。基本上,我有以下问题。我有一个功能:
def f(a, b, c, d, e):
...
我还有第二个函数,它调用f
,但从a
推断出一些参数:
def g(a, **kwargs):
b = deduce(a)
return f(a, b, **kwargs)
现在用functools.wraps
从f
复制kwargs
会很好。但是我不能直接使用,因为f
的签名和g
的签名不匹配:参数b
在g
中是固定的。我希望有一种方法可以清楚地表明g
是f
的函数,但b
是推导出来的。
我希望问题是清楚的;我很想听听你的想法!
我认为在这种情况下,只要有一个明确的文档字符串将是所有你需要的。functools.wraps
对于要完全取代原始定义的装饰器更有用。对于一个单独的函数(g
和f
仍独立引用)你不应该使用。
您还需要处理用户没有提供关键字args的情况。例如,下面的代码将会失败:
>>> f(1,2,3,4,5)
>>> g(1,3,4,5)
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: g() takes 1 positional argument but 4 were given
为g
工作,你需要做:
def g(a, *args, **kwargs):
b = deduce(a)
return f(a, b, *args, **kwargs)
另外,如果您担心f
的签名在g
下面发生变化,那么您还应该防止开始参数的顺序发生变化。
为了使f
在更改签名时尽可能好用,可能值得完全禁止使用位置参数。(还添加了漂亮的文档字符串):
def f(*, a, b, c, d, e):
...
def g(*, a, **kwargs):
"""
Acts in the same way as function `f`, however, argument `b` must
be omitted as it will be deduced from `a`.
"""
return f(**kwargs, a=a, b=deduce(a))
这也保持了一个很好的副作用,即禁止您在显式定义b
时调用g
:
>>> g(a=1, b=2, c=3, d=4, e=0)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 14, in g
TypeError: __main__.f() got multiple values for keyword argument 'b'
my .
如果这是一个将被许多人使用的库,那么如果在签名中显式列出值会更好。这将允许静态分析工具在运行前捕获错误。
当您有很多参数时,这很容易出错,但是使用pyi
存根文件时,您只需要这样做一次(而不是实际执行的代码)。实际代码中的样板保持最小,并且预期的定义对用户来说变得清晰。
g.py
def g(*, a, **kwargs):
"""
Acts in the same way as function `f`, however, argument `b` must
be omitted as it will be deduced from `a`.
"""
return f(**kwargs, a=a, b=deduce(a))
g.pyi
def g(*, a, c, d, e): ...