使用functool.wraps时重写函数签名(在帮助中)



我正在为一个具有functools.wraps的函数创建一个包装器。我的包装器具有覆盖默认参数的效果(它不做任何其他事情(:

def add(*, a=1, b=2):
"Add numbers"
return a + b
@functools.wraps(add)
def my_add(**kwargs):
kwargs.setdefault('b', 3)
return add(**kwargs)

my_add定义的行为与相同

@functools.wraps(add)
def my_add(*, a=1, b=3):
return add(a=a, b=b)

只是我不必手动键入参数列表。

但是,当我运行help(my_add)时,我看到了add的帮助字符串,它有错误的函数名和参数b:的错误默认参数

add(*, a=1, b=2)
Add numbers

如何覆盖此help()输出中的函数名和默认参数?

(或者,有没有一种不同的方法来定义my_add,例如使用一些magic函数my_add = magic(add, func_name='my_add', kwarg_defaults={'b': 3})来实现我想要的功能?(

让我试着解释一下发生了什么。

当您调用help函数时,这将使用inspect模块请求有关您的函数的信息。因此,您必须更改函数签名,才能更改默认参数。

现在,这不是建议的,也不是人们经常喜欢的,但谁会关心这一点呢?所提供的解决方案被认为是有技巧的,可能不适用于所有版本的Python。因此,您可能需要重新考虑help函数的重要性……无论如何,让我们先解释一下它是如何实现的,然后是代码和测试用例。


复制功能

现在我们要做的第一件事是复制整个函数,这是因为我只想更改新函数的签名,而不是原始函数的签名。这将使新的my_add签名(和默认值(与原始add函数解耦。

参见:

  • 如何创建python函数的副本
  • 如何在Python中制作函数的深度副本

关于如何做到这一点的想法(我稍后会展示我的版本(。

复制/更新签名

下一步是获得函数签名的副本,因为这篇文章非常有用。除了我们必须调整签名参数以匹配新的关键字默认参数的部分。

为此,我们必须更改mappingproxy的值,这可以在对inspect.signature(g)的返回值运行调试器时看到。到目前为止,这只能通过更改私有变量(带前导下划线的值_private(来完成。因此,该解决方案将被认为是黑客,不能保证能够承受可能的更新。话虽如此,让我们看看解决方案!


完整代码

import inspect
import types
import functools

def update_func(f, func_name='', update_kwargs: dict = None):
"""Based on http://stackoverflow.com/a/6528148/190597 (Glenn Maynard)"""
g = types.FunctionType(
code=f.__code__,
globals=f.__globals__.copy(),
name=f.__name__,
argdefs=f.__defaults__,
closure=f.__closure__
)
g = functools.update_wrapper(g, f)
g.__signature__ = inspect.signature(g)
g.__kwdefaults__ = f.__kwdefaults__.copy()
# Adjust your arguments
for key, value in (update_kwargs or {}).items():
g.__kwdefaults__[key] = value
g.__signature__.parameters[key]._default = value
g.__name__ = func_name or g.__name__
return g

def add(*, a=1, b=2):
"Add numbers"
return a + b
my_add = update_func(add, func_name="my_add", update_kwargs=dict(b=3))

示例

if __name__ == '__main__':
a = 2

print("*" * 50, f"nMy addn", )
help(my_add)
print("*" * 50, f"nOriginal addn", )
help(add)
print("*" * 50, f"nResults:"
f"ntMy add      : a = {a}, return = {my_add(a=a)}"
f"ntOriginal add: a = {a}, return = {add(a=a)}")

输出

************************************************** 
My add
Help on function my_add in module __main__:
my_add(*, a=1, b=3)
Add numbers
************************************************** 
Original add
Help on function add in module __main__:
add(*, a=1, b=2)
Add numbers
************************************************** 
Results:
My add      : a = 2, return = 5
Original add: a = 2, return = 4

用法

  • f:是要更新的函数
  • func_name:可选为函数的新名称(如果为空,则保留旧名称(
  • update_kwargs:是一个字典,包含要更新的默认参数的键和值

注释

  • 解决方案是使用copy变量制作字典的完整副本,这样就不会对原始add函数产生影响
  • _default值是一个私有变量,可以在未来版本的python中更改

相关内容

  • 没有找到相关文章

最新更新