我有一个在这个函数中创建的lambda对象:
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
self.record(lambda s:
s.add_url_rule(rule, endpoint, view_func, **options))
使用lambda函数对象的func_closure
我可以访问lambda函数的闭包范围:
(<cell at 0x3eb89f0: str object at 0x2fb4378>,
<cell at 0x3eb8a28: function object at 0x3cb3a28>,
<cell at 0x3eb8a60: str object at 0x3ebd090>,
<cell at 0x3eb8b08: dict object at 0x3016ec0>)
仔细观察(每个cell_contents
对象的cell
属性)向我展示:
>>> [c.cell_contents for c in func.func_closure]
['categoryDisplay',
<function indico.web.flask.util.RHCategoryDisplay>,
'/<categId>/',
{}]
这个lambda函数是由这个调用创建的:
add_url_rule('/<categId>/', 'categoryDisplay', rh_as_view(RHCategoryDisplay))
如您所见,顺序与函数的参数顺序或lambda中使用参数的顺序不匹配。虽然我可以根据其类型/内容轻松找出哪个元素是什么,但我想以更清洁的方式进行。
所以我的问题是:如何将它与原始变量名称相关联(或者至少在函数参数的情况下将位置关联起来)?
答案 0 :(得分:13)
闭包是由LOAD_CLOSURE
字节码创建的,其顺序与字节码的排序顺序相同:
>>> dis.dis(add_url_rule)
2 0 LOAD_FAST 0 (self)
3 LOAD_ATTR 0 (record)
6 LOAD_CLOSURE 0 (endpoint)
9 LOAD_CLOSURE 1 (options)
12 LOAD_CLOSURE 2 (rule)
15 LOAD_CLOSURE 3 (view_func)
18 BUILD_TUPLE 4
21 LOAD_CONST 1 (<code object <lambda> at 0x10faec530, file "<stdin>", line 2>)
24 MAKE_CLOSURE 0
27 CALL_FUNCTION 1
30 POP_TOP
31 LOAD_CONST 0 (None)
34 RETURN_VALUE
所以顺序是在编译时确定的,compiler_make_closure()
;此函数使用func.func_code.co_freevars
元组作为指南,以相同的顺序列出闭包。
makecode
中创建代码对象时设置了 func.func_code.co_freevars
,并且元组是从python字典的键生成的,因此顺序是任意的,对于字典来说是常见的。如果你很好奇,dict是用compiler_enter_scope()
构建的,使用编译器符号表中指定的所有自由变量中的dictbytype()
utility function,它本身就是一个python字典。
因此,闭包的顺序确实是任意的(哈希表有序),你可以使用func.func_code.co_freevars
元组(可以看作编译器处理字典键的顺序的记录)到将名称附加到闭包:
dict(zip(func.func_code.co_freevars, (c.cell_contents for c in func.func_closure)))
答案 1 :(得分:5)
感谢Freenode上的#python中的YHg1s我明白了:func_code.co_freevars
是一个元组,包含闭包中元素的变量名。
>>> func.func_code.co_freevars
('endpoint', 'view_func', 'rule', 'options')
因此,创建一个dict映射名称到闭包值很容易:
>>> dict(zip(func.func_code.co_freevars,
(c.cell_contents for c in func.func_closure)))
{'endpoint': 'categoryDisplay',
'options': {},
'rule': '/<categId>/',
'view_func': <function indico.web.flask.util.RHCategoryDisplay>}