【问题标题】:List and generator comprehensions with class variabes in conditional statement [duplicate]条件语句中带有类变量的列表和生成器理解[重复]
【发布时间】:2015-10-13 08:46:13
【问题描述】:

考虑以下代码 sn-p:

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 个值并在每次下一个 next() 调用时引发 StopIteration

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

【问题讨论】:

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


【解决方案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

【讨论】:

    猜你喜欢
    • 2020-05-29
    • 1970-01-01
    • 2016-10-06
    • 2018-04-20
    • 2019-11-03
    • 1970-01-01
    • 1970-01-01
    • 2020-01-22
    • 2018-04-17
    相关资源
    最近更新 更多