类中直接使用的推导式的作用域问题探究
Python类体中列表推导式的作用域问题解析
核心原因是Python 3中列表推导式会创建独立的嵌套作用域,而类体的临时命名空间不会被这个嵌套作用域继承,同时推导式的不同部分(迭代器表达式、过滤条件、映射表达式)的求值环境存在差异。
分场景拆解
1. 无过滤条件的推导式能运行的原因
class Foo: a = (1,2,3) b = [2*x for x in a]
这里的a是推导式的迭代器表达式,它会在类体的临时命名空间中直接求值:类定义执行时,先把a解析为类体里的变量,生成迭代器后,推导式才进入自己的作用域执行循环和映射操作。此时迭代器已准备完成,推导式内部无需再访问a,因此不会报错。
2. 带过滤条件的推导式报错的原因
class Foo: a = (1,2,3) b = [2*x for x in (2,3,4) if x in a]
这里的if x in a是推导式的过滤条件,它会在推导式自身的独立作用域中执行。Python推导式的作用域只会继承全局命名空间,或是函数内的闭包作用域,但类体的临时命名空间不属于这两类——推导式内部无法找到类体中的a,因此触发NameError。
3. 普通for循环能运行的原因
class Foo: a = (1,2,3) b = [] for x in (2,3,4): if x in a: b.append(x)
普通for循环直接在类体的临时命名空间中执行,和类变量a处于同一作用域,自然可以直接访问a,不存在作用域隔离的问题。
可行的解决办法
如果要在类体的推导式中访问类变量,推荐借助类体中的函数闭包:
class Foo: a = (1,2,3) def _build_b(): # 函数闭包可以捕获类体的命名空间 return [2*x for x in (2,3,4) if x in a] b = _build_b()
内容的提问来源于stack exchange,提问作者George Mauer
相关产品推荐
相关产品推荐

