多个python装饰器检索原始函数

时间:2015-06-04 09:19:13

标签: python decorator

我有一个运行检查的类,每个函数都执行检查。我曾想过装饰者增加额外的条件,但这可能不明智。

我理解装饰器的概念,每个装饰器将其装饰的功能作为输入,并返回到下一个装饰的功能。这适用于返回字符串的函数,因为输出可以很容易地被装饰器编辑并返回给下一个装饰器。

我想要做的是修改函数属性,例如为检查不要运行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,这不是我想要的。

有没有办法检索要装饰的原始功能?或者在这种情况下装饰器的使用不相关?什么是解决方案?

非常感谢

2 个答案:

答案 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__属性。