首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何确定创建闭包的函数?

如何确定创建闭包的函数?
EN

Stack Overflow用户
提问于 2016-12-22 15:53:20
回答 2查看 215关注 0票数 6

Python2.7中的这段代码在func周围创建了一个闭包,包含了par变量:

代码语言:javascript
复制
def creator(par):
    def func(arg):
        return par + arg
    return func

它可以这样使用:

代码语言:javascript
复制
f = creator(7)
f(3)               # returns 10

在运行时,是否有一种方法可以获取定义闭包的函数的名称?也就是说:我只能访问f变量,可以获得f闭包是在creator函数中定义的信息吗?

我正在使用Python2.7。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-12-25 00:11:23

我怀疑它不能在Python2.7中完成,至少不能直接完成。下面是函数f的打印得很好的内容,减去一些明显无关的条目:

代码语言:javascript
复制
>>> pprint({key: getattr(f, key) for key in dir(f)})
{'__call__': <method-wrapper '__call__' of function object at 0x7f9d22aefb18>,
 '__class__': <type 'function'>,
 '__closure__': (<cell at 0x7f9d22af1fd8: int object at 0x1311128>,),
 '__code__': <code object func at 0x7f9d22d3b230, file "<stdin>", line 2>,
 '__defaults__': None,
 '__dict__': {},
 '__doc__': None,
 '__module__': '__main__',
 '__name__': 'func',
 'func_closure': (<cell at 0x7f9d22af1fd8: int object at 0x1311128>,),
 'func_code': <code object func at 0x7f9d22d3b230, file "<stdin>", line 2>,
 'func_defaults': None,
 'func_dict': {},
 'func_doc': None,
 'func_globals': {'__builtins__': <module '__builtin__' (built-in)>,
                  '__doc__': None,
                  '__name__': '__main__',
                  '__package__': None,
                  'creator': <function creator at 0x7f9d22aefaa0>,
                  'f': <function func at 0x7f9d22aefb18>,
                  'pprint': <function pprint at 0x7f9d22aefc08>},
 'func_name': 'func'}

唯一有趣的键是func_closure (哪个is __closure__)和func_code (哪个is __code__),但它们都没有帮助。

闭包是cell对象的一个元组,每个对象都包含关闭环境中的键值对的值。f.func_closure中只有一个值,即par变量的值:

代码语言:javascript
复制
>>> repr(f.func_closure[0])
<cell at 0x7f9d22af1fd8: int object at 0x1311128>
>>> f.func_closure[0].cell_contents
7

cell不包含对闭包的创建者、使用闭包的函数或甚至封闭环境本身的引用。(封闭环境的元素似乎是根据它们在cell对象元组中的位置检索的。)

函数代码对象更接近,但也没有命名它的创建者。减去明显不相关的条目后,它包含:

代码语言:javascript
复制
>>> pprint({k: getattr(f.func_code, k) for k in dir(f.func_code)})
{'__class__': <type 'code'>,
 '__doc__': 'code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n      varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\nCreate a code object.  Not for the faint of heart.',
 'co_argcount': 1,
 'co_cellvars': (),
 'co_code': '\x88\x00\x00|\x00\x00\x17S',
 'co_consts': (None,),
 'co_filename': '<stdin>',
 'co_firstlineno': 2,
 'co_flags': 19,
 'co_freevars': ('par',),
 'co_lnotab': '\x00\x01',
 'co_name': 'func',
 'co_names': (),
 'co_nlocals': 1,
 'co_stacksize': 2,
 'co_varnames': ('arg',)}

这包含闭包变量('co_freevars': ('par',),)的名称,以及creator ('co_name': 'func',)中函数的名称,但不包含名称或对外部函数的任何引用。

部分解

有一种方法可以识别封闭函数的外部函数,如果两者都有引用的话。创建函数的函数代码对象将包含对关闭函数的代码对象的引用:

代码语言:javascript
复制
>>> pprint({k: getattr(creator.func_code, k) for k in dir(creator.func_code)})
{'__class__': <type 'code'>,
 '__doc__': 'code(argcount, nlocals, stacksize, flags, codestring, constants, names,\n      varnames, filename, name, firstlineno, lnotab[, freevars[, cellvars]])\n\nCreate a code object.  Not for the faint of heart.',
 'co_argcount': 1,
 'co_cellvars': ('par',),
 'co_code': '\x87\x00\x00f\x01\x00d\x01\x00\x86\x00\x00}\x01\x00|\x01\x00S',
 'co_consts': (None,
               <code object func at 0x7f9d22d3b230, file "<stdin>", line 2>),
 'co_filename': '<stdin>',
 'co_firstlineno': 1,
 'co_flags': 3,
 'co_freevars': (),
 'co_lnotab': '\x00\x01\x0f\x02',
 'co_name': 'creator',
 'co_names': (),
 'co_nlocals': 2,
 'co_stacksize': 2,
 'co_varnames': ('par', 'func')}

因为元组creator.func_code.co_consts 包含对 f.func_code的引用,所以可以确定creatorf的来源。

代码语言:javascript
复制
>>> f.func_code in creator.func_code.co_consts
True
>>> f.func_code is creator.func_code.co_consts[1]
True

creator返回的每个函数都使用相同的代码对象(它们的差异存储在cell对象中,而不是代码对象中):

代码语言:javascript
复制
>>> g = creator(10)
>>> g.func_code is f.func_code is creator.func_code.co_consts[1]
True

因此,如果可以缩小潜在源的范围,例如在globals()dir(some_class)中的值,则可以测试每个源是否是f的“父”

代码语言:javascript
复制
def is_creator(f, contender):
    target = f.func_code
    try:
        constants = contender.func_code.co_consts
    except AttributeError:
        return False
    for constant in constants:
        if constant is target:
            return True
    return False

def find_creators(f, contenders):
    for contender in contenders:
        if is_creator(f, contender):
            yield contender
    return

>>> is_creator(f, creator)
True
>>> is_creator(g, creator)
True

>>> is_creator(f, max)
False
>>> is_creator(f, g)
False
>>> is_creator(f, 'Seriously?')
False
>>> is_creator(f, None)
False

>>> list(find_creators(f, globals().values()))
[<function creator at 0x7f9d22aefaa0>]

>>> builtins = [getattr(__builtins__, s) for s in dir(__builtins__)]
>>> list(find_creators(f, builtins))
[]

这种类型很糟糕,因为它没有指向创建者,它只是识别了创建者,如果你已经找到了它。此外,如果有人使用creator.__code__构建一个冒名顶替者,也可能会被愚弄:

代码语言:javascript
复制
def impostor(bogus):
    def need_a_free_variable_in_impostors_func_code(unused):
        return bogus - unused
    return need_a_free_variable_in_impostors_func_code

>>> creator(3)(7)
10
>>> impostor(3)(7)
-4
>>> is_creator(f, impostor)
False

>>> impostor.__code__ = creator.__code__
>>> impostor(3)(7)
10
>>> is_creator(f, impostor)
True
>>> list(find_creators(f, globals().values()))
[<function creator at 0x7f9d22aefaa0>, <function impostor at 0x7f9d1bf7f8c0>]

有问题的改进

一旦潜在的创造者被发现,还有其他的线索,但它们并不是真正的证据。示例包括:

关闭变量'par'的名称以空闲变量的形式出现在f中,在creator中显示为“单元”变量:

代码语言:javascript
复制
>>> f.func_code.co_freevars[0] in creator.func_code.co_cellvars
True

f的名称(即'func',而不是'f')出现在创建者的函数代码对象中。(函数代码对象是不可变的,因此f.func_code.co_name必须是创建时分配给f的原始名称。从那时起,f.__name__就可以重新分配了。f.func_code --整个代码对象--也可以--但这一点并不常见。

代码语言:javascript
复制
>>> f.func_code.co_name in creator.func_code.co_varnames
True

因为函数定义可以被深嵌套--意味着在最里面的函数中不同的自由变量可以在不同的外部函数中定义(记录在co_cellvars中)--我不认为为这些函数添加检查会使is_creator“更聪明”。

票数 2
EN

Stack Overflow用户

发布于 2016-12-22 15:59:15

您可以为此使用__qualname__,它表示限定函数名

代码语言:javascript
复制
def creator(par):
    def func(arg):
        return par + arg
    return func

>>> f = creator(7)
>>> f.__qualname__
'creator.<locals>.func'
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41287019

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档