我认为你应该使用装饰器 a 来装饰装饰器 b,它可以让你在决策函数的帮助下打开或关闭装饰器 b。
这听起来很复杂,但想法相当简单。
所以假设你有一个装饰器记录器:
from functools import wraps
def logger(f):
@wraps(f)
def innerdecorator(*args, **kwargs):
print (args, kwargs)
res = f(*args, **kwargs)
print res
return res
return innerdecorator
这是一个非常无聊的装饰器,我有十几个这样的,缓存器,记录器,注入东西的东西,基准测试等。我可以用 if 语句轻松扩展它,但这似乎是一个糟糕的选择;因为那我得换十几个装饰器,一点都不好玩。
那么该怎么办?让我们更上一层楼。假设我们有一个装饰器,它可以装饰一个装饰器?这个装饰器看起来像这样:
@point_cut_decorator(logger)
def my_oddly_behaving_function
这个装饰器接受记录器,这不是一个很有趣的事实。但它也有足够的权力来选择是否应该将记录器应用于 my_oddly_behave_function。我称它为 point_cut_decorator,因为它具有面向方面编程的某些方面。切入点是一组位置,其中一些代码(建议)必须与执行流程交织在一起。切点的定义通常在一个地方。这种技术似乎非常相似。
我们如何实现它的决策逻辑。好吧,我选择创建一个函数,它接受被装饰者、装饰者、文件和名称,它只能说明是否应该应用装饰器。这些是坐标,足以非常精确地确定位置。
这是 point_cut_decorator 的实现,我选择将决策函数实现为一个简单的函数,你可以扩展它以让它从你的设置或配置中决定,如果你对所有 4 个坐标使用正则表达式,你最终会得到一些非常强大的:
from functools import wraps
myselector 是决策函数,在 true 上应用装饰器,在 false 上不应用。参数是文件名、模块名、装饰对象,最后是装饰器。这使我们能够以细粒度的方式切换行为。
def myselector(fname, name, decoratee, decorator):
print fname
if decoratee.__name__ == "test" and fname == "decorated.py" and decorator.__name__ == "logger":
return True
return False
这装饰了一个函数,检查 myselector,如果 myselector 说继续,它会将装饰器应用于函数。
def point_cut_decorator(d):
def innerdecorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
if myselector(__file__, __name__, f, d):
ps = d(f)
return ps(*args, **kwargs)
else:
return f(*args, **kwargs)
return wrapper
return innerdecorator
def logger(f):
@wraps(f)
def innerdecorator(*args, **kwargs):
print (args, kwargs)
res = f(*args, **kwargs)
print res
return res
return innerdecorator
这就是你使用它的方式:
@point_cut_decorator(logger)
def test(a):
print "hello"
return "world"
test(1)
编辑:
这是我谈到的正则表达式方法:
from functools import wraps
import re
如您所见,我可以在某处指定一些规则,这些规则决定是否应用装饰器:
rules = [{
"file": "decorated.py",
"module": ".*",
"decoratee": ".*test.*",
"decorator": "logger"
}]
然后我遍历所有规则,如果规则匹配则返回 True,如果规则不匹配则返回 false。通过在生产中使规则为空,这不会使您的应用程序减慢太多:
def myselector(fname, name, decoratee, decorator):
for rule in rules:
file_rule, module_rule, decoratee_rule, decorator_rule = rule["file"], rule["module"], rule["decoratee"], rule["decorator"]
if (
re.match(file_rule, fname)
and re.match(module_rule, name)
and re.match(decoratee_rule, decoratee.__name__)
and re.match(decorator_rule, decorator.__name__)
):
return True
return False