如何将func_closure条目映射到变量名称?

时间:2013-06-17 10:23:35

标签: python python-2.x cpython

我有一个在这个函数中创建的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中使用参数的顺序不匹配。虽然我可以根据其类型/内容轻松找出哪个元素是什么,但我想以更清洁的方式进行。

所以我的问题是:如何将它与原始变量名称相关联(或者至少在函数参数的情况下将位置关联起来)?

2 个答案:

答案 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>}