保留装饰功能的签名 [英] Preserving signatures of decorated functions

查看:51
本文介绍了保留装饰功能的签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Suppose I have written a decorator that does something very generic. For example, it might convert all arguments to a specific type, perform logging, implement memoization, etc.

这里是一个示例:

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_ints
def funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z

>>> funny_function("3", 4.0, z="5")
22

一切都很好至今。但是,有一个问题。装饰的函数不保留原始函数的文档:

Everything well so far. There is one problem, however. The decorated function does not retain the documentation of the original function:

>>> help(funny_function)
Help on function g in module __main__:

g(*args, **kwargs)

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

Fortunately, there is a workaround:

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_ints
def funny_function(x, y, z=3):
    """Computes x*y + 2*z"""
    return x*y + 2*z

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

This time, the function name and documentation are correct:

>>> help(funny_function)
Help on function funny_function in module __main__:

funny_function(*args, **kwargs)
    Computes x*y + 2*z

但是仍然存在一个问题:函数签名错误。信息 * args,** kwargs几乎无用。

But there is still a problem: the function signature is wrong. The information "*args, **kwargs" is next to useless.

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

What to do? I can think of two simple but flawed workarounds:

1-在文档字符串中包含正确的签名:

1 -- Include the correct signature in the docstring:

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

这很糟糕,因为重复。签名仍不会在自动生成的文档中正确显示。更新功能很容易,而不必更改文档字符串,也不会打错字。 [是的,我知道文档字符串已经复制了函数主体。请忽略此; ]

This is bad because of the duplication. The signature will still not be shown properly in automatically generated documentation. It's easy to update the function and forget about changing the docstring, or to make a typo. [And yes, I'm aware of the fact that the docstring already duplicates the function body. Please ignore this; funny_function is just a random example.]

2-不使用修饰符,或对每个特定签名使用专用修饰符:

2 -- Not use a decorator, or use a special-purpose decorator for every specific signature:

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

这对于具有相同签名的一组函数很好用,但通常没有用。正如我在一开始所说的那样,我希望能够完全通用地使用装饰器。

This works fine for a set of functions that have identical signature, but it's useless in general. As I said in the beginning, I want to be able to use decorators entirely generically.

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

I'm looking for a solution that is fully general, and automatic.

所以问题是:创建装饰后的函数签名后,有没有办法编辑它?

So the question is: is there a way to edit the decorated function signature after it has been created?

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

Otherwise, can I write a decorator that extracts the function signature and uses that information instead of "*kwargs, **kwargs" when constructing the decorated function? How do I extract that information? How should I construct the decorated function -- with exec?

还有其他方法吗?

推荐答案


  1. 安装装饰器模块:

$ pip install decorator


  • args_as_ints()的自适应定义:

    import decorator
    
    @decorator.decorator
    def 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)
    
    @args_as_ints
    def funny_function(x, y, z=3):
        """Computes x*y + 2*z"""
        return x*y + 2*z
    
    print 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 3.4 +



    来自stdlib的 functools.wraps() 自Python 3.4起保留签名:


    Python 3.4+

    functools.wraps() from stdlib preserves signatures since Python 3.4:

    import functools
    
    
    def args_as_ints(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            args = [int(x) for x in args]
            kwargs = dict((k, int(v)) for k, v in kwargs.items())
            return func(*args, **kwargs)
        return wrapper
    
    
    @args_as_ints
    def funny_function(x, y, z=3):
        """Computes x*y + 2*z"""
        return x*y + 2*z
    
    
    print(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
    

    functools.wraps()至少从Python 2.5起,但是它不在那里保留签名:

    functools.wraps() is available at least since Python 2.5 but it does not preserve the signature there:

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

    注意: * args,** kwargs x,y,z = 3

    这篇关于保留装饰功能的签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆