什么时候使用装饰器和装饰工厂?

时间:2015-02-24 10:56:30

标签: python python-2.7 decorator factory python-decorators

这有点令人困惑,装饰器不带参数,而装饰工厂接受参数并仍然装饰一个函数

何时使用说明会很有帮助。

编辑: 困惑就是一个例子:

def before_run(func):
    print "hello from before run"
    def handle_arg(a,b):
        if(a>0):
            a= 100
        return func(a,b)

    return handle_arg

@before_run
def running_func(a,b):
    print "a",a,"b", b
    return a+b

编辑:有没有办法通过添加日志选项(true或false)使用装饰工厂来做到这一点?

3 个答案:

答案 0 :(得分:6)

装饰器 factory 只是一个可调用的,可以生成实际的装饰器。它用于配置'装饰师。

所以而不是:

@decorator
def decorated_function():

您使用:

@decorator_factory(arg1, arg2)
def decorated_function():

并且该调用将返回使用的实际装饰器。

这通常是通过将装饰器嵌套在另一个函数中,并使用该新外部函数的参数来调整返回的装饰器的行为来实现的。

对于你的样本装饰器,缩进你的装饰器(你可能想重命名它以减少混淆)并将它包装在一个带有logging参数的工厂函数中:

def before_run(logging=True):
    def decorator(func):
        print "hello from before run"
        def handle_arg(a,b):
            if(a>0):
                if logging:
                    print "Altering argument a to 100"
                a = 100
            return func(a,b)

        return handle_arg

    return decorator

我将原来的before_run()装饰器功能重命名为decorator,以明确这是工厂生产的装饰器。它最后返回;这个装饰器函数使用logging作为闭包来打开或关闭登录。

答案 1 :(得分:1)

如果您希望通过参数动态控制装饰器的行为(就像使用任何常规函数一样),您将使用装饰器工厂。例如,假设我想要一个在调用函数之前打印消息的装饰器。我可以做类似的事情:

# Our decorator:
def print_message_decorator(func):
    # The wrapper (what we replace our decorated function with):
    def wrapper(*args, **kwargs):
        print('A function is being called.')
        result = func(*args, **kwargs)
        return result
    return wrapper

@print_message_decorator
def add(a, b):
    return a + b

@print_message_decorator
def subtract(a, b):
    return a - b

现在,如果我致电addsubtract,则每个人都会打印A function is being called.

然而,现在说,我实际上想要动态生成装饰器,并且我希望能够自定义每个装饰函数打印出来的消息。我通过让装饰师成为装饰工厂来解决这个问题。

# Our decorator factory:
def print_message_decorator_factory(msg):
    # The decorator it creates:
    def print_message_decorator(func):
        # The wrapper (what we replace our decorated function with):
        def wrapper(*args, **kwargs):
            print(msg)
            result = func(*args, **kwargs)
            return result
        return wrapper
    return print_message_decorator

@print_message_decorator_factory('Calling the add function.')
def add(a, b):
    return a + b

@print_message_decorator_factory('Calling the subtract function.')
def subtract(a, b):
    return a - b

现在,如果我致电add,则会打印Calling the add function.,如果我致电subtract,则会打印Calling the subtract function.

答案 2 :(得分:0)

与python中的装饰器确实存在一些混淆。

这是由于以下事实:

  • 带有参数的装饰器实际上不是装饰器,而是 其他人指出的装饰工厂。所以要实现一个装饰器 可以在不带参数的情况下调用它,这有点棘手。

  • 人们倾向于认为装饰器一定是函数包装器,就像您的示例一样。但这不是事实:装饰器可以用其他东西(甚至不是函数或类!)完全替换装饰的函数或类。

为了简化装饰器的开发,我写了decopatch。有了它,您不必在意:您的装饰器可以正确处理无括号和带括号的调用。

它支持两种开发样式:嵌套(如python装饰工厂中的样式)和 flat (少一层嵌套)。这是您的示例在 flat 模式下实现的方式:

from __future__ import print_function
from decopatch import function_decorator, DECORATED
from makefun import wraps

@function_decorator
def before_run(func=DECORATED):
    # (1) create a signature-preserving wrapper
    @wraps(func)
    def _func_wrapper(*f_args, **f_kwargs):
        print("hello from before run")
        if f_kwargs['a'] > 0:
            f_kwargs['a'] = 100
        return func(*f_args, **f_kwargs)

    # (2) return it
    return _func_wrapper

最后,它支持另一种开发风格,我称之为 double-flat ,该风格专门用于创建保留签名的函数包装器。您的示例将这样实现:

from __future__ import print_function
from decopatch import function_decorator, WRAPPED, F_ARGS, F_KWARGS

@function_decorator
def before_run(func=WRAPPED, f_args=F_ARGS, f_kwargs=F_KWARGS):
    # this is directly the signature-preserving wrapper body
    print("hello from before run")
    if f_kwargs['a'] > 0:
        f_kwargs['a'] = 100
    return func(*f_args, **f_kwargs)

在两种情况下,您都可以检查其是否正常运行:

@before_run
def running_func(a, b):
    print("a", a, "b", b)
    return a + b

assert running_func(-1, 2) == 1
assert running_func(1, 2) == 102

有关详细信息,请参阅documentation