【问题标题】:Problem when calling decorator with arguments使用参数调用装饰器时出现问题
【发布时间】:2019-10-20 19:21:50
【问题描述】:

我正在尝试测试下面的装饰器 test_params 以进行单元测试,但是当我尝试在函数 test_testparams 中调用它时,出现错误:

TypeError: wrapper() takes exactly 1 argument (0 given)

是因为它在期待自己吗?

import unittest
import functools


def test_params(kwargs_lst):
    '''allows to parametrise tests easily using a decorator'''

    def decorator(fn):
        @functools.wraps(fn)
        def wrapper(self):
            for kwargs in kwargs_lst:
                fn(self, **kwargs)

        return wrapper

    return decorator


TEST_PARAMS_ARGS = [
    {'arg1': 1, 'arg2': None, 'arg3': "test"},
]

RECORDER = []


class TestParamsClass(object):
    def record_args(self, arg1, arg2, arg3):
        RECORDER.append({'arg1': arg1, 'arg2': arg2, 'arg3': arg3})


class DecoratorTest(unittest.TestCase):
    def setUp(self):
        pass

    def test_testparams(self):
        obj = TestParamsClass()

        decorated_fn = test_params(TEST_PARAMS_ARGS)(obj.record_args)
        decorated_fn()
        print("RECORDED:{}".format(RECORDER))

为了全面披露,我已经尝试过传递 self 参数:

decorated_fn(obj)

但这会报错:

TypeError: record_args() got multiple values for keyword argument 'arg1'

我也尝试过使用:

from functools import partial
decorated_fn = test_params(TEST_PARAMS_ARGS)(partial(obj.record_args, self=obj))

这给了我错误:

AttributeError: 'functools.partial' object has no attribute '__module__'

有 self 参数的原因是它通常在测试函数中用于传递参数,并且测试函数在 unittest.TestCase 中。

【问题讨论】:

  • 好吧,你调用decorated_fn(),没有为预期的self参数传递参数……
  • @deceze,你能举个例子吗,我试着把 obj 放在 self 上,但不能让它工作。我已经编辑了我的答案。
  • 你为什么把self放在那里?我的意思是,不应该只是def wrapper(),没有任何争论吗?执行 fn 也不应该采用 self - self 仅在类中相关,其中函数是方法,因此它们在一些 self 对象上执行 - 当你执行 obj.record_args 时,你已经给出了函数已设置self
  • 我有 self 的原因是它通常用于函数内部测试以将测试参数作为 kwargs 传递,我会更新

标签: python python-2.7


【解决方案1】:

这与what Python methods really are 有关。

通常你会将装饰器应用于类声明中的函数,即:

class TestParamsClass(object):
    @test_params(TEST_PARAMS_ARGS)
    def record_args(self, arg1, arg2, arg3):
        RECORDER.append({'arg1': arg1, 'arg2': arg2, 'arg3': arg3})

在这种情况下test_params(或更确切地说是内部decorator函数)将接收record_args 函数作为参数。但在这里:

decorated_fn = test_params(TEST_PARAMS_ARGS)(obj.record_args)

您传递的是一个Method 对象(参见上面的链接),在调用record_args() 函数时,__call__ 方法已经将obj 作为self 参数注入。

如果您的 test_param 装饰器应该专门用于方法(或更准确地说,用于仅用作方法的函数),您可以更改测试以装饰方法的函数:

decorated_fn = test_params(TEST_PARAMS_ARGS)(obj.record_args.__func__)

当然也可以手动传递obj 作为参数:

decorated_fn(obj)

否则,如果您希望能够在函数和方法上使用此装饰器,您可以将内部 wrapper 函数签名从 self 更改为 *args(并将其传递给 fn 以便它可以适应这两种情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多