3

我正在使用鼻子测试生成器功能在不同的上下文中运行相同的测试。由于每个测试都需要以下样板:

class TestSample(TestBase):

    def test_sample(self):
        for context in contexts:
            yield self.check_sample, context

    def check_sample(self, context):
        """The real test logic is implemented here"""
        pass

我决定编写以下装饰器:

def with_contexts(contexts=None):        
    if contexts is None:
        contexts = ['twitter', 'linkedin', 'facebook']

    def decorator(f):
        @wraps(f)
        def wrapper(self, *args, **kwargs):
            for context in contexts:
                yield f, self, context # The line which causes the error
        return wrapper
    return decorator

装饰器的使用方式如下:

class TestSample(TestBase):  

    @with_contexts()
    def test_sample(self, context):
        """The real test logic is implemented here"""
        var1 = self.some_valid_attribute

当测试执行时,会抛出一个错误,指定正在访问的属性不可用。但是,如果我将调用该方法的行更改为以下内容,则它可以正常工作:

yield getattr(self, f.__name__), service

我知道上面的代码片段创建了一个绑定方法,其中第一个self被手动传递给函数。但是,据我的理解,第一个片段也应该可以正常工作。如果有人能澄清这个问题,我将不胜感激。

问题的标题通常与在装饰器中调用实例方法有关,但我保留了特定于我的上下文的描述。

4

2 回答 2

3

您可以使用functools.partial将包装的函数绑定到self,就像方法一样:

from functools import partial

def decorator(f):
    @wraps(f)
    def wrapper(self, *args, **kwargs):        
        for context in contexts:
            yield partial(f, self), context
    return wrapper

现在您正在生成部分,当调用 as 时yieldedvalue(context),将调用f(self, context).

于 2012-12-10T09:35:08.127 回答
0

据我所知,有些东西不适合。首先,你的装饰器就像

def with_contexts(contexts=None):        
    if contexts is None:
        contexts = ['twitter', 'linkedin', 'facebook']

    def decorator(f):
        @wraps(f)
        def wrapper(self, *args, **kwargs):
            for context in contexts:
                yield f, self, context # The line which causes the error
        return wrapper
    return decorator

但你像使用它一样

@with_contexts
def test_sample(self, context):
    """The real test logic is implemented here"""
    var1 = self.some_valid_attribute

这是错误的: this 调用with_context(test_sample),但你需要with_context()(test_sample). 也一样

@with_contexts()
def test_sample(self, context):
    """The real test logic is implemented here"""
    var1 = self.some_valid_attribute

即使你不提供contexts论据。

其次,你修饰了错误的函数:你的用法表明该函数为每个上下文test生成函数。check你要包装的函数完成了检查函数的工作,但你必须在测试函数之后命名它。

应用self到一个方法可以用partialMartijn 写的,但它也可以用 Python 在后台的方式来完成:

method.__get__(self, None)

或者更好

method.__get__(self, type(self))

你可以达到同样的效果。(也许你的原始版本也可以,产生要调用的函数和要使用的参数。我不清楚这是它的工作方式。)

于 2012-12-10T10:02:52.120 回答