可以执行或跳过python装饰器,具体取决于先前的装饰器结果吗?

时间:2014-11-04 23:16:41

标签: python dependencies decorator execution python-decorators

我从这个stack_overflow_entry中了解到,Python装饰器按照它们在源中出现的顺序应用。

那么下面的代码片段应该如何表现?

@unittest.skip("Something no longer supported")
@skipIf(not something_feature_enabled, "Requires extra crunchy cookies to run")
def test_this():
  ....

第一个装饰者(如下所述)要求测试跑者完全跳过test_this()

@unittest.skip("Something no longer supported")

第二个装饰者要求测试运行员有条件地跳过test_this()

@skipIf(not something_feature_enabled, "Requires extra crunchy cookies to run")

这是否意味着test_this根本不会被运行,除非我们先放置条件跳过装饰器?

另外,Python中有没有办法定义装饰器的依赖执行? e.g。

@skipIf("Something goes wrong")
@skipIf(not something_feature_enabled, "Requires extra crunchy cookies to run")
@log
@send_email
def test_this():
  ....

如果@log@send_email,我们的想法是启用@skipIf("Something goes wrong")true的执行。

道歉,如果我遗漏了一些非常明显的东西。

1 个答案:

答案 0 :(得分:2)

我认为你可能错过了一个关键点:装饰器只是一个传递函数并返回函数的函数。

所以,这些是相同的:

@log
def test_this():
    pass

def test_this():
    pass
test_this = log(test_this)

同样地:

@skip("blah")
def test_this():
    pass

def test_this():
    pass
test_this = skip("blah")(test_this)

一旦你理解了这一点,你的所有问题都变得非常简单。


首先,是的,skip(…)用于装饰skipIf(…)(test),所以如果它跳过装饰的东西,test将永远不会被调用。


定义调用者调用顺序的方法是按照你想要的顺序编写它们。

如果你想动态地这样做,你可以通过动态应用装饰器来实现。例如:

for deco in sorted_list_of_decorators:
    test = deco(test)

  

另外,Python中有没有办法定义装饰器的依赖执行?

不,他们都被执行了。与您所询问的内容更相关,每个装饰器都应用于装饰函数,而不是装饰器。

但是你总是可以将装饰器传递给条件装饰器:

def decorate_if(cond, deco):
    return deco if cond else lambda f: f

然后:

@skipIf("Something goes wrong")
@decorate_if(something_feature_enabled, log)
@decorate_if(something_feature_enabled, send_email)
def test_this():
    pass

简单,对吧?

现在,如果log是真实的,则会应用send_emailsomething_feature_enabled装饰器;

。否则,装饰器不会以任何方式修饰该函数,只会将其保持不变。

但是如果你不能通过装饰器怎么办,因为功能已经装饰好了?好吧,如果你定义每个装饰器来展示它包裹的功能,你总是可以打开它。如果你总是使用functools.wraps(如果没有其他理由你通常会这样做 - 而且即使你有这样的理由你也可以用这种方式轻松模仿),包裹的函数总是以{{{ 1}}。因此,您可以编写一个装饰器,有条件地删除最外层的装饰:

__wrapped__

同样,如果你想动态地做这件事,你可能会动态装饰。因此,一个更简单的解决方案就是跳过你不想要的装饰器,在它们被应用之前从def undecorate_if(cond): def decorate(f): return f.__unwrapped__ if cond else f return decorate 迭代中移除它们。