![](/img/trans.png)
[英]How can I determine which variable to use for a defined function in python
[英]How can I determine the function in which a closure was created?
Python 2.7中的这段代码在func
周围创建了一个闭包,包含了par
变量:
def creator(par):
def func(arg):
return par + arg
return func
它可以像这样使用:
f = creator(7)
f(3) # returns 10
在运行时,有没有办法获取定义闭包的函数的名称? 那就是:只能访问f
变量,我能获得f
封闭是在creator
函数中定义的信息吗?
我使用的是Python 2.7。
您可以使用__qualname__
来表示限定的函数名称
def creator(par):
def func(arg):
return par + arg
return func
>>> f = creator(7)
>>> f.__qualname__
'creator.<locals>.func'
我怀疑它不能在Python 2.7中完成,至少不能直接完成。 这是你的函数f
的漂亮打印内容,减去一些明显不相关的条目:
>>> 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
变量的值:
>>> repr(f.func_closure[0])
<cell at 0x7f9d22af1fd8: int object at 0x1311128>
>>> f.func_closure[0].cell_contents
7
cell
不包含对闭包的创建者的引用,使用闭包的函数,甚至包含封闭的环境本身。 (封闭环境的元素似乎是根据它们在cell
对象元组中的位置来检索的。)
功能代码对象更接近,但也没有命名其创建者。 减去明显不相关的条目,它包含:
>>> 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',
),但不包含名称或任何引用外在的功能。
如果您同时参考两者,则有一种方法可以识别封闭函数的外部函数。 创建函数的函数代码对象将包含对已关闭函数的代码对象的引用:
>>> 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
是f
的来源,因为元组creator.func_code.co_consts
包含对f.func_code
的引用 :
>>> f.func_code in creator.func_code.co_consts
True
>>> f.func_code is creator.func_code.co_consts[1]
True
creator
返回的每个函数都使用相同的代码对象(它们的差异存储在cell
对象中,而不是代码对象中):
>>> g = creator(10)
>>> g.func_code is f.func_code is creator.func_code.co_consts[1]
True
所以,如果你可以缩小潜在的资源,比如globals()
或dir(some_class)
,你可以测试它们中的每一个,看看它是否是f
的“父”:
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__
构建一个冒名顶替者,它可能会被愚弄:
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
为“cell”变量:
>>> 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
---整个代码对象 - - 但这并不常见。)
>>> f.func_code.co_name in creator.func_code.co_varnames
True
因为函数定义可以深度嵌套 - 意味着最内层函数中的不同自由变量可以在不同的外部函数中定义(记录在co_cellvars
) - 我不认为添加对这些函数的检查会使is_creator
“更聪明”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.