条件语句中具有类变量的列表和生成器理解

时间:2015-10-13 08:46:13

标签: python python-2.7 generator python-2.x

请考虑以下代码段:

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()

这有什么不同?在每种情况下如何解决名称以及为什么会出现差异?

1 个答案:

答案 0 :(得分:1)

问题是生成器表达式在它们自己的命名空间中运行,因此它们无法访问类范围中的名称(类ab等类。)

这在PEP 227 -

中给出
  

无法访问类范围中的名称。名称在最里面的封闭函数范围内解析。如果类定义出现在嵌套作用域链中,则解析过程将跳过类定义。

因此,当您尝试访问生成器表达式中的类变量时,您将获得NameError

解决此问题的一种方法是通过类C.aC.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