更改生成器的__name__ [英] Changing the __name__ of a generator

查看:122
本文介绍了更改生成器的__name__的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

进行以下设置:

def mapper(f):
    def wrapper(items):
        for x in items:
            yield f(x)
    wrapper.__name__ = f.__name__ # This has no effect!
    return wrapper

@mapper
def double(x):
    return x * 2

装饰器按预期工作:

>>> [x for x in double([1,2,3])]
[2, 4, 6]

但是它的__name__不是所需的double:

>>> double([1,2]).__name__
"wrapper"

是否可以强制生成器的名称? 另外,是否可以在生成器对象中四处浏览并检索名称double?

Is it possible to force the name of the generator? Alternatively, is it possible to dig around in the generator object and retrieve the name double?

推荐答案

您只能将名称复制到函数中.产生的生成器对象仍然使用在编译时设置的旧名称.

You only copied across the name to the function. The generator object produced still uses the old name set at compile time.

您必须每次设置该名称正在生成一个新的生成器对象;不幸的是,对于生成器,该属性为只读:

You would have to set that name each time a new generator object is being produced; unfortunately, the attribute is read-only for generators:

>>> 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

该名称包含在代码对象中:

The name is baked into the code object:

>>> gen.__code__.co_name
'gen'

因此,唯一可以更改此方法的方法是重新构建代码对象(以及扩展功能):

so the only way you can change this is to re-build the code object (and by extension, the function):

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替换旧名称:

This creates a new function and code object, using new_name to replace the old 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开始,生成器对象从函数对象而不是从代码对象co_name属性获取其__name__,请参见问题#21205 ;该属性在该过程中变为可写.

As of Python 3.5, generator objects take their __name__ from the function object rather than from their code object co_name attribute, see issue #21205; the attribute became writable in the process.

这篇关于更改生成器的__name__的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆