什么时候使用装饰器和装饰器工厂?
when to use decorator and decorator factory?
装饰器不带参数而装饰器工厂带参数并仍然装饰函数,这有点令人困惑
描述什么时候使用会很有帮助。
编辑:
困惑在于一个例子:
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)使用装饰器工厂来做到这一点?
装饰器 factory 只是一个生成实际装饰器的可调用对象。它用于使 'configure' 装饰器成为可能。
所以代替:
@decorator
def decorated_function():
您将使用:
@decorator_factory(arg1, arg2)
def decorated_function():
并且该调用将 return 实际使用的装饰器。
这通常是通过将装饰器嵌套在另一个函数中,并使用新的外部函数的参数来调整装饰器的行为来实现的 returned。
对于您的示例装饰器,缩进您的装饰器(您可能想重命名它以减少混淆)并将其包装在采用 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
,以明确这是工厂生产的装饰器。最后是 returned;此装饰器函数使用 logging
作为闭包来打开或关闭日志记录。
如果您希望通过参数动态控制装饰器的行为(就像任何常规函数一样),您可以使用装饰器工厂。例如,假设我想要一个在调用函数之前打印一条消息的装饰器。我可以做类似的事情:
# 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
,每个都将打印 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.
python.
中的装饰器确实有点混乱
这是因为:
带参数的装饰器实际上不是装饰器,而是一个
装饰器工厂,如其他人所述。所以要实现一个装饰器
可以在没有参数和有参数的情况下调用,这有点棘手。
人们倾向于认为装饰器必然是函数包装器,就像您的示例中那样。但事实并非如此:装饰器可以用其他东西(甚至不是函数或 class!)完全替换装饰函数或 class。
为了简化装饰器开发,我写了 decopatch。有了它,您就不必担心:您的装饰器可以正确处理无括号和带括号的调用。
它支持两种开发风格:nested(如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
装饰器不带参数而装饰器工厂带参数并仍然装饰函数,这有点令人困惑
描述什么时候使用会很有帮助。
编辑: 困惑在于一个例子:
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)使用装饰器工厂来做到这一点?
装饰器 factory 只是一个生成实际装饰器的可调用对象。它用于使 'configure' 装饰器成为可能。
所以代替:
@decorator
def decorated_function():
您将使用:
@decorator_factory(arg1, arg2)
def decorated_function():
并且该调用将 return 实际使用的装饰器。
这通常是通过将装饰器嵌套在另一个函数中,并使用新的外部函数的参数来调整装饰器的行为来实现的 returned。
对于您的示例装饰器,缩进您的装饰器(您可能想重命名它以减少混淆)并将其包装在采用 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
,以明确这是工厂生产的装饰器。最后是 returned;此装饰器函数使用 logging
作为闭包来打开或关闭日志记录。
如果您希望通过参数动态控制装饰器的行为(就像任何常规函数一样),您可以使用装饰器工厂。例如,假设我想要一个在调用函数之前打印一条消息的装饰器。我可以做类似的事情:
# 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
,每个都将打印 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.
python.
中的装饰器确实有点混乱这是因为:
带参数的装饰器实际上不是装饰器,而是一个 装饰器工厂,如其他人所述。所以要实现一个装饰器 可以在没有参数和有参数的情况下调用,这有点棘手。
人们倾向于认为装饰器必然是函数包装器,就像您的示例中那样。但事实并非如此:装饰器可以用其他东西(甚至不是函数或 class!)完全替换装饰函数或 class。
为了简化装饰器开发,我写了 decopatch。有了它,您就不必担心:您的装饰器可以正确处理无括号和带括号的调用。
它支持两种开发风格:nested(如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