Python 何时使用装饰器和装饰工厂?

Python 何时使用装饰器和装饰工厂?,python,python-2.7,decorator,factory,python-decorators,Python,Python 2.7,Decorator,Factory,Python Decorators,这使得decorator不带参数,而decoratorfactory带参数,并且仍然装饰函数有点混淆 当使用when描述时会很有帮助 编辑: 困惑在于一个例子: 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

这使得decorator不带参数,而decoratorfactory带参数,并且仍然装饰函数有点混淆

当使用when描述时会很有帮助

编辑: 困惑在于一个例子:

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
编辑:有没有一种方法可以使用decorator工厂通过添加日志选项(true或false)来实现这一点?

decorator工厂只是一个生成实际decorator的可调用函数。它用于使“配置”装饰器成为可能

因此,不是:

@decorator
def decorated_function():
您可以使用:

@decorator_factory(arg1, arg2)
def decorated_function():
这个调用将返回实际使用的decorator

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

对于示例decorator,缩进decorator(您可能希望重命名它以减少混淆),并将其包装在一个工厂函数中,该函数接受
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函数重命名为
decorator
,以明确这就是工厂生产的decorator。它在最后被返回;此decorator函数使用
日志记录
作为关闭开关来打开或关闭日志记录。

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

# 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
现在,如果我调用
add
subtract
,每个函数都将打印
一个正在调用的函数。

但是,现在我想说,我实际上希望动态生成装饰器,并且我希望能够自定义每个装饰函数打印出来的消息。我解决这个问题的办法是让我的装饰师改为一家装饰工厂

# 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
它会打印
调用add函数。
如果我调用
subtract
它会打印
调用subtract函数。

python中的装饰程序确实有点混乱

这是因为:

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

  • 人们倾向于认为装饰器必然是函数包装器,如您的示例中所示。但事实并非如此:装饰器可以用其他东西(甚至不是函数或类!)完全替换装饰过的函数或类

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

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

最后,它支持一种额外的开发风格,我称之为双平面,专门用于创建保留签名的函数包装器。您的示例的实现方式如下:

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

有关详细信息,请参阅。

您已经自己回答了。接受参数的装饰器是一个装饰器工厂。@MartijnPieters,但我无法创建一个完全适合它的案例场景。谢谢,我不知道你在问什么。以论点为例。@MartijnPieters很抱歉不清楚,我想在decorator和decorator工厂之间寻找一个区别因素。还做了一个有问题的编辑。仍然不清楚。你为什么要把你的装潢师复杂化,把它包装在工厂里?它需要什么论据?有道理。谢谢
@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