保留装饰功能的签名

保留装饰功能的签名

假设我编写了一个装饰器来做一些非常通用的东西。例如,它可能会将所有参数转换为特定类型,执行日志记录,实现memoization等。

这是一个例子:

def args_as_ints(f):
    def g(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    return g@args_as_intsdef funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z>>> funny_function("3", 4.0, z="5")22

到目前为止一切都很好。然而,有一个问题。装饰函数不保留原始函数的文档:

>>> help(funny_function)Help on function g in module __main__:g(*args, **kwargs)

幸运的是,有一个解决方法:

def args_as_ints(f):
    def g(*args, **kwargs):
        args = [int(x) for x in args]
        kwargs = dict((k, int(v)) for k, v in kwargs.items())
        return f(*args, **kwargs)
    g.__name__ = f.__name__
    g.__doc__ = f.__doc__    return g@args_as_intsdef funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z

这次,函数名称和文档是正确的:

>>> help(funny_function)Help on function funny_function in module __main__:funny_function(*args, **kwargs)
    Computes x*y + 2*z

但仍存在一个问题:功能签名是错误的。信息“* args,** kwargs”几乎没用。

该怎么办?我可以想到两个简单但有缺陷的解决方法:

1 - 在docstring中包含正确的签名:

def funny_function(x, y, z=3):
    """funny_function(x, y, z=3) -- computes x*y + 2*z"""
    return x*y + 2*z

这很糟糕,因为重复。签名仍无法在自动生成的文档中正确显示。更新函数很容易,忘记更改文档字符串或打字错误。[ 是的,我知道docstring已经复制了函数体这一事实。请忽略这一点; funny_function只是一个随机的例子。]

2 - 不使用装饰器,或为每个特定签名使用专用装饰器:

def funny_functions_decorator(f):
    def g(x, y, z=3):
        return f(int(x), int(y), z=int(z))
    g.__name__ = f.__name__
    g.__doc__ = f.__doc__    return g

这适用于具有相同签名的一组函数,但一般来说它没用。正如我在开始时所说,我希望能够完全使用装饰器。

我正在寻找一种完全通用且自动化的解决方案。

所以问题是:有没有办法在创建装饰函数签名后对其进行编辑?

否则,我可以编写一个提取器来提取函数签名并在构造装饰函数时使用该信息而不是“* kwargs,** kwargs”吗?如何提取该信息?我应该如何构建装饰函数 - 使用exec?

还有其他方法吗?


收到一只叮咚
浏览 403回答 3
3回答

陪伴而非守候

这是通过Python的标准库functools和特定functools.wraps功能来解决的,该功能旨在“ 将包装函数更新为包装函数 ”。但是,它的行为取决于Python版本,如下所示。应用于问题的示例,代码如下所示:from functools import wrapsdef args_as_ints(f):     @wraps(f)      def g(*args, **kwargs):         args = [int(x) for x in args]         kwargs = dict((k, int(v)) for k, v in kwargs.items())         return f(*args, **kwargs)     return g@args_as_intsdef funny_function(x, y, z=3):     """Computes x*y + 2*z"""     return x*y + 2*z在Python 3中执行时,会产生以下结果:>>> funny_function("3", 4.0, z="5")22>>> help(funny_function)Help on function funny_function in module __main__:funny_function(x, y, z=3)     Computes x*y + 2*z它唯一的缺点是在Python 2中,它不会更新函数的参数列表。在Python 2中执行时,它将产生:>>> help(funny_function)Help on function funny_function in module __main__:funny_function(*args, **kwargs)     Computes x*y + 2*z

慕森王

有一个装饰器模块,decorator你可以使用装饰器:@decoratordef args_as_ints(f, *args, **kwargs):     args = [int(x) for x in args]     kwargs = dict((k, int(v)) for k, v in kwargs.items())     return f(*args, **kwargs)然后保留方法的签名和帮助:>>> help(funny_function)Help on function funny_function in module __main__:funny_function(x, y, z=3)     Computes x*y + 2*z
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Python