请考虑以下代码段:
class C(object):
a = 0
b = 1
seq = [1, 2, 4, 16, 17]
list_comp = [a if v%2 else b for v in seq]
gen_comp = (a if v%2 else b for v in seq)
上面的代码解释正常。绑定到类变量的打印对象导致:
print C.list_comp # [0, 1, 1, 1, 0]
print C.gen_comp # <generator object <genexpr> at ...>
可悲的是 - 尝试从NameError
next(C.gen_comp) # NameError: global name 'a' is not defined
预期的行为应该与列表理解类似 - 它应该产生5个值,并在每个下一个StopIteration
调用时引发next()
。
这有什么不同?在每种情况下如何解决名称以及为什么会出现差异?
答案 0 :(得分:1)
问题是生成器表达式在它们自己的命名空间中运行,因此它们无法访问类范围中的名称(类a
或b
等类。)
无法访问类范围中的名称。名称在最里面的封闭函数范围内解析。如果类定义出现在嵌套作用域链中,则解析过程将跳过类定义。
因此,当您尝试访问生成器表达式中的类变量时,您将获得NameError
。
解决此问题的一种方法是通过类C.a
或C.b
等类访问所需的值。由于生成器表达式中的表达式仅在调用next()
时执行,因此我们可以确定当时已定义C
类。示例 -
>>> class C(object):
... a = 0
... b = 1
... seq = [1, 2, 4, 16, 17]
... list_comp = [a if v%2 else b for v in seq]
... gen_comp = (C.a if v%2 else C.b for v in seq)
...
>>> next(C.gen_comp)
0
>>> next(C.gen_comp)
1
>>> next(C.gen_comp)
1
>>> next(C.gen_comp)
1
>>> next(C.gen_comp)
0
注意,Python 3.x中的列表理解也会出现同样的问题,因为在Python 3.x中,列表推导有自己的范围。有关详细信息,请参阅Accessing class variables from a list comprehension in the class definition。