【问题标题】:multiple python decorators retrieve original function多个python装饰器检索原始函数
【发布时间】:2015-08-18 19:54:23
【问题描述】:

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

我理解装饰器的概念,每个装饰器都将其装饰的函数作为输入,然后将装饰的函数返回给下一个。例如,这对于返回字符串的函数来说效果很好,因为输出可以很容易地被装饰器编辑并返回到下一个装饰器。

我想要做的是修改函数属性,例如设置一个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,这不是我想要的。

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

非常感谢

【问题讨论】:

  • "但是 @auth_required 应用于 assignOrder,这不是我想要的。"实际上,auth_required 应用于“assignOrder 返回的内容”,而不是assignOrder 本身。你真正的真正问题是什么?
  • 感谢您的回答;我的问题是我不能同时设置authenticated 标志和分配order。我编辑了我的问题,以便更清楚
  • 恐怕我不明白你的 auth_required 装饰器应该如何工作???
  • 我在别处对用户进行身份验证,并用身份验证结果填充此装饰器参数:True 表示允许,False 表示禁止。
  • 我仍然不明白它应该如何工作,抱歉。你定义和应用auth_require()的方式,你的装饰函数变成check_authentication(f)。这个函数的作用是: 1. 检查is_authenticated 是否为真——这将永远为真,因为在这个阶段is_authenticated 是原始函数(你用auth_required() 装饰的那个)并且function 对象有一个布尔上下文中的真值 -,然后 2. 返回它的参数 f。这显然不是你所期望的,是吗???

标签: python decorator


【解决方案1】:

没有解决问题的通用方法。装饰器没有内置的合作机制,甚至不需要保留对原始函数的引用。

因此,您需要自己制定一个协议,例如通过制作assignOrder将原函数保存到do_assignment

然后在is_authenticated 中,您必须查看传递的函数背后是否有“真实”函数,并使用它来代替。

【讨论】:

    【解决方案2】:

    尽管您声明自己不理解装饰器,但您的代码似乎令人困惑。

    如果你这样做,我希望你明白这一点:

    def auth_required(is_authenticated):
        def check_authentication(f):
            if is_authenticated:
                return f
        return check_authentication
    
    @auth_required(something)
    def my_function():
        ....
    

    意味着在导入时检查“某物”变量,并且只检查一次,并且您的修饰函数将被抑制 - 即名称“mu_function”将绑定到None,导致任何代码中的错误试图这样称呼。如果您替换 return 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__ 属性。

    【讨论】:

      猜你喜欢
      • 2010-11-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-14
      • 2016-11-24
      • 1970-01-01
      • 2013-06-15
      • 2014-07-21
      相关资源
      最近更新 更多