为何fun.__code__.co_names未包含全部全局变量?fun1案例分析
为什么fun1的__code__.co_names不包含dict_de_en?
先看第一段测试代码:
dict_de_en = {'we':'wir', 'love':'lieben', 'cake':'kuchen'} def fun1(sentence): return " ".join([dict_de_en[w] for w in sentence.split()]) def fun2(sentence): dd = dict_de_en return " ".join([dd[w] for w in sentence.split()]) print("f1:", fun1("we love cake")) # "wir lieben kuchen" print("f2:", fun2("we love cake")) # ditto print("n1", fun1.__code__.co_names) # ('join', 'split') print("n2", fun2.__code__.co_names) # ('dict_de_en', 'join', 'split')
运行后发现fun1.__code__.co_names里没有dict_de_en,但下面这段代码的结果却符合预期:
dict_de_en = {'we':'wir', 'love':'lieben', 'cake':'kuchen'} def fun3(word): return dict_de_en[word] def fun4(word): dd = dict_de_en return dd[word] print("f3:", fun3("we")) # "wir" print("f4:", fun4("we")) # ditto print("n3", fun3.__code__.co_names) # ('dict_de_en',) print("n4", fun4.__code__.co_names) # ('dict_de_en',)
背景:运营Python MOOC时,自动评分系统需要检测函数中全局变量的使用,其他情况正常,但fun1的情况出现异常。
核心原因:列表推导式是独立的嵌套代码对象
Python中,列表推导式会被编译成一个独立的嵌套代码对象,不属于函数主代码的一部分:
fun1里的dict_de_en是在列表推导式内部被引用的,这个推导式有自己的__code__对象,dict_de_en会被记录在这个嵌套对象的co_names中,而非fun1自身的co_names。- 你可以通过以下代码验证这一点:
# 遍历fun1的常量,找到嵌套的列表推导式代码对象 for const in fun1.__code__.co_consts: if isinstance(const, type(fun1.__code__)): print(const.co_names) # 输出 ('dict_de_en',)
fun2里的dict_de_en是在函数主代码中被引用(赋值给dd),所以会被记录在fun2自身的co_names里。fun3直接在函数主代码中引用dict_de_en,自然出现在它的co_names中;fun4同样是在主代码里引用全局变量赋值,因此也会被记录。
对自动评分系统的改进建议
要准确检测全局变量的使用,不能只检查函数自身的__code__.co_names,还需要遍历函数__code__.co_consts中的所有嵌套代码对象(包括列表推导式、生成器表达式、嵌套函数等),检查这些子代码对象的co_names,这样才能覆盖所有全局变量引用的场景。
内容的提问来源于stack exchange,提问作者Berry Boessenkool
相关产品推荐
相关产品推荐

