基于this thread,我有以下
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
我在这里做错了什么?
答案 0 :(得分:3)
好吧,我不明白为什么你在这里使用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
答案 1 :(得分:2)
你做错了很多事,所以让我们看看它:
在您的代码中,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