微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 functools.wraps 时覆盖函数签名在帮助中 示例输出用法

如何解决使用 functools.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(*,b=3):
    return add(a=a,b=b)

除了我不必手动输入参数列表。

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

add(*,b=2)
    Add numbers

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

(或者,是否有不同的方式来定义 my_add,例如使用一些 magic 函数 my_add = magic(add,func_name='my_add',kwarg_defaults={'b': 3}) 来做我想做的事?)

解决方法

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

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

现在这不是建议或通常首选的东西,但谁在乎呢?提供的解决方案被认为是 hacky,可能不适用于所有版本的 Python。因此,您可能需要重新考虑 help 函数的重要性...无论如何,让我们先解释一下它是如何完成的,然后是代码和测试用例。


复制功能

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

见:

有关如何执行此操作的想法(稍后我将展示我的版本)。

复制/更新签名

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

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


完整代码

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 add\n",)
    help(my_add)

    print("*" * 50,f"\nOriginal add\n",)
    help(add)

    print("*" * 50,f"\nResults:"
                    f"\n\tMy add      : a = {a},return = {my_add(a=a)}"
                    f"\n\tOriginal add: a = {a},return = {add(a=a)}")

输出

************************************************** 
My add

Help on function my_add in module __main__:

my_add(*,b=3)
    Add numbers

************************************************** 
Original add

Help on function add in module __main__:

add(*,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 的未来版本中更改。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。