【问题标题】:Understanding my own decorator了解我自己的装饰器
【发布时间】:2015-01-29 17:35:17
【问题描述】:

我编写了一个可以正常工作的装饰器,但我通过反复试验偶然发现了正确的解决方案,而我对装饰器的了解很少告诉我,有些东西没有很好地定义。

情况是我正在模拟一个 Rest Api 来做一些 TDD,而这个 Rest 是一个令牌安全性的背后。因此,在发出任何请求之前,我首先必须获得我的用户令牌。我正在使用 httpretty 来模拟 API。

到目前为止,我必须在每个测试用例中注册 uri,一个用于模拟 /token 资源,另一个用于测试任何其他资源。但是我发现这很麻烦,所以我提供了一个解决方案来编写一个简单的装饰器来模拟 /token,然后只需要模拟测试的资源。

这是我目前正在工作的装饰器...

def activate_security(func):
    def test_case(test_case):
        httpretty.enable()
        uri = 'http://{}:{}/token'.format(HOST, PORT)
        httpretty.register_uri(httpretty.GET, uri,
                               body=dumps({'token': 'dummy_token'}),
                               content_type='application/json')
        test_case()
        httpretty.disable()
    return test_case

这就是所谓的。

@activate_security
@httpretty.activate
def test_case_one(self):
    #Test case here

我必须将 test_case 参数传递给内部函数,因为没有它它将无法工作,而 test_case 是 test_case_one 方法,我认为它会在 func 参数中传递,但外部范围内的 func 成立test_case 内存中的对象。

不应该 func 装饰器的返回值吗?如果我这样做,装饰器将不起作用。什么时候给内部函数传递那个参数?

【问题讨论】:

    标签: python decorator python-decorators httpretty


    【解决方案1】:

    您正在装饰方法,因此您生成的包装函数需要一个 self 参数,就像在类中使用的普通函数一样。

    所有不同之处在于您使用了与self 参数test_case 不同的名称。碰巧,实例是可调用的,调用它会运行测试,所以你实际上是在做self() 来运行测试再次

    只需将参数命名为self 并将其传递给包装函数即可:

    def activate_security(func):
        def wrapper(self):
            httpretty.enable()
            uri = 'http://{}:{}/token'.format(HOST, PORT)
            httpretty.register_uri(httpretty.GET, uri,
                                   body=dumps({'token': 'dummy_token'}),
                                   content_type='application/json')
            func(self)
            httpretty.disable()
        return wrapper
    

    然后wrapper() 函数替换原来的test_case_one 函数,并且在运行测试时wrapper() 函数绑定到测试用例实例,并将作为self 传递该实例;在您的包装器中,您可以调用 unbound func(),只需将 self 传递给它。

    出于调试目的,将一些函数属性从包装函数复制到包装器通常会更好; @functools.wraps() decorator 可以为您处理这些细节:

    import functools
    
    def activate_security(func):
        @functools.wraps(func)
        def wrapper(self):
            httpretty.enable()
            uri = 'http://{}:{}/token'.format(HOST, PORT)
            httpretty.register_uri(httpretty.GET, uri,
                                   body=dumps({'token': 'dummy_token'}),
                                   content_type='application/json')
            func(self)
            httpretty.disable()
        return wrapper
    

    【讨论】:

    • 非常感谢。特别是关于 functools.wraps
    猜你喜欢
    • 2020-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-18
    • 2018-10-03
    • 2022-01-07
    • 2017-09-20
    • 2014-10-17
    相关资源
    最近更新 更多