我有一个运行检查的类,每个函数都执行检查。我曾想过装饰者增加额外的条件,但这可能不明智。
我理解装饰器的概念,每个装饰器将其装饰的功能作为输入,并返回到下一个装饰的功能。这适用于返回字符串的函数,因为输出可以很容易地被装饰器编辑并返回给下一个装饰器。
我想要做的是修改函数属性,例如为检查不要运行don't exec
标志,require auth
仅在授予身份验证时执行检查,或者更改函数属性order
,按顺序检查启动。
# I return f only if is_authenticated flag is True
def auth_required(is_authenticated):
def check_authentication(f):
if is_authenticated:
return f
return check_authentication
# I edit order variable
def assignOrder(order):
def do_assignment(f):
f.order = order
return f
return do_assignment
# I instanciate Checks class and provide authentication;
# login and password will be tried and is_authenticat flag set accordingly
c = Checks(target, login, password)
# I sort on order variable and launch checks
functions = sorted(
[
getattr(c, field) for field in dir(c)
if hasattr(getattr(c, field), 'order')
],key = (lambda field : field.order)
)
for function in functions:
function()
# I assign decorators so that order variable is set
# I would like auth_required triggers launch if auth is performed
@auth_required(True)
@assignOrder(100)
def check_shares(self):
# check defined here
这对assignOrder很有用,我的检查按正确的顺序启动。
但@auth_required
适用于assignOrder
,这不是我想要的。
有没有办法检索要装饰的原始功能?或者在这种情况下装饰器的使用不相关?什么是解决方案?
非常感谢
答案 0 :(得分:0)
没有解决问题的通用方法。装饰者没有内置的合作设施,甚至不需要保留对原始功能的引用。
所以你需要自己提出一个协议,例如:将assignOrder
原始函数保存到do_assignment
。
然后在is_authenticated
中,您必须查看是否存在"真实"传递后的函数,然后使用它。
答案 1 :(得分:0)
你的代码似乎令人困惑,尽管你声明你做了不明显的装饰者。
我希望你不要理解,如果你这样做:
def auth_required(is_authenticated):
def check_authentication(f):
if is_authenticated:
return f
return check_authentication
@auth_required(something)
def my_function():
....
表示在导入时检查“something”变量,并且只检查一次,并且您的修饰函数将被抑制 - 即名称“mu_function”将绑定到None
,导致任何错误尝试调用它的代码。如果替换返回None以返回不执行任何操作的函数,要解决该错误,在重新加载包含该函数的模块之前,这仍然是不可更改的(在测试场景中,这通常意味着再次运行所有内容)
所以,这不仅仅是错误的 - 它表明你完全被装饰者所搞糊涂了。
依靠全局变量以这种方式打开和关闭功能会更简单:
from functools import wraps
def auth_required(f):
@wraps(f)
def wrapper (*args, **kw):
if is_authenticated:
return f(*args, **kw)
return None
return wrapper
现在,这些装饰器可以通过在调用之间的运行时更改is_authenticated
模块级变量来随意“打开”和“关闭”装饰功能 - 无需重新加载模块。
另外,回到关于“多个python装饰器检索原始函数”的标题问题:
注意我在上面的包装器代码中添加了functools.wraps
装饰器。最近Python中的这个函数在装饰函数上设置了一个__wrapped__
属性,该属性指向您的原始装饰函数。
因此,如果所有装饰器都表现良好并在其包装器上应用functools.wraps
,则始终可以访问最内层的函数
使用其__wrapped__
属性。