您只在名稱上覆制到函數中。生成的生成器對象仍然使用編譯時設置的舊名稱。
您將不得不每次設置名稱正在生成新的生成器對象;不幸的是,屬性是只讀發電機:
>>> def gen():
... yield None
...
>>> gen().__name__
'gen'
>>> gen().__name__ = 'foo'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: attribute '__name__' of 'generator' objects is not writable
名稱烘焙成代碼對象:
>>> gen.__code__.co_name
'gen'
所以你可以改變這一點的唯一辦法是重新構建代碼對象(並且通過擴展,函數):
def rename_code_object(func, new_name):
code_object = func.__code__
function, code = type(func), type(code_object)
return function(
code(
code_object.co_argcount, code_object.co_nlocals,
code_object.co_stacksize, code_object.co_flags,
code_object.co_code, code_object.co_consts,
code_object.co_names, code_object.co_varnames,
code_object.co_filename, new_name,
code_object.co_firstlineno, code_object.co_lnotab,
code_object.co_freevars, code_object.co_cellvars),
func.__globals__, new_name, func.__defaults__, func.__closure__)
這將創建一個新的功能和代碼對象使用new_name
,以取代舊名稱:
>>> new_name = rename_code_object(gen, 'new_name')
>>> new_name.__name__
'new_name'
>>> new_name().__name__
'new_name'
>>> new_name()
<generator object new_name at 0x10075f280>
在你的裝飾使用此:
def mapper(f):
def wrapper(items):
for x in items:
yield f(x)
return rename_code_object(wrapper, f.__name__)
演示:
>>> def mapper(f):
... def wrapper(items):
... for x in items:
... yield f(x)
... return rename_code_object(wrapper, f.__name__)
...
>>> @mapper
... def double(x):
... return x * 2
...
>>> double([1, 2])
<generator object double at 0x10075f370>
>>> list(double([1, 2]))
[2, 4]
對於Python 3.5,發電機對象採取他們__name__
從函數對象而不是其代碼對象co_name
屬性,請參見issue #21205;該屬性在該過程中變爲可寫。
我想你需要'functools.wraps'。看到這個:http://stackoverflow.com/questions/308999/what-does-functools-wraps-do/309000#309000 – Pynchia
@Pynchia:這完全相同的事情(加上其他屬性副本)。 –
我認爲這裏真正的問題是*「你怎麼能'''給一個發電機對象命名?」*;你想在裝飾器中這樣做的事實是偶然的。 – jonrsharpe