当前位置: 代码迷 >> python >> 如何使用装饰器提供函数元数据?
  详细解决方案

如何使用装饰器提供函数元数据?

热度:90   发布时间:2023-06-21 10:51:42.0

基于 ,我有以下内容

import functools
def predicate(author, version, **others):
    def _predicate(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            func.meta = {
                'author':  author,
                'version': version
            }
            func.meta.update(others)
            func(*args, **kwargs)
        return wrapper
    return _predicate

但我无法得到简单的用例

@predicate('some author', 'some version')
def second_of_two(a, b):
    return b

按预期工作:

>>> second_of_two.meta['author']
'some author'
>>> second_of_two(1, 2)
2

我在这做错了什么?

好吧,我不明白为什么你在这里使用functools东西,而你需要使它成为一个带参数的简单装饰器:

def predicate(author, version, **others):
    def _predicate(func):
        func.meta = {
            'author':  author,
            'version': version
        }
        func.meta.update(others)
        return func
    return _predicate

@predicate('foo', 'bar')
def myfunc(i,j):
    return i+j

print myfunc.meta
print myfunc(1,2)

得到:

{'version': 'bar', 'author': 'foo'}
3

你错误地做了很多事情,所以让我们看看它:

在你的代码中, predicate是装饰器的生成器; 实际的装饰者是_predicate 装饰器接受一个函数并返回一个函数; 后者将被分配到添加原始代码的位置。 因此,使用@predicate(…) def second_of_two ,名称second_of_two将获得装饰器返回的值。

那么让我们看看你的装饰师做了什么:

def _predicate(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        …
    return wrapper

它构建了一个新的内部函数,使用functool.wraps (这是一个很好的实践),然后返回该函数。 到现在为止还挺好; 所以second_of_two正确获取了修饰函数。

现在,包装函数做了什么呢?

def wrapper(*args, **kwargs):
    func.meta = {
        'author':  author,
        'version': version
    }
    func.meta.update(others)
    func(*args, **kwargs)

包装函数在原始函数对象上设置元数据。 这是第一个错误; 您在原始函数上设置元数据,以后不再参考。 second_of_two是包装函数,因此它没有那些元函数。 此外,您只在函数运行时设置这些元数据。 因此,如果您在包装器函数上正确设置元数据,那么只有在您调用一次后它仍然可用。 第三个错误是你调用原始函数并简单地抛弃它的返回值。 这就是为什么你没有得到任何输出。

那么你该怎么做呢? 首先,将元数据设置在包装函数之外,并将其设置在您返回的函数上。 在包装函数内部,返回原始函数的返回值:

def predicate(author, version, **others):
    def _predicate(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)

        wrapper.meta = {
            'author':  author,
            'version': version
        }
        wrapper.meta.update(others)

        return wrapper
    return _predicate

而现在,您的wrapper函数不再具有任何特殊功能,因此您只需使用原始函数:

def predicate(author, version, **others):
    def _predicate(func):
        func.meta = {
            'author':  author,
            'version': version
        }
        func.meta.update(others)
        return func
    return _predicate
  相关解决方案