Python 类中的列表理解范围规则是啥? [复制]

Posted

技术标签:

【中文标题】Python 类中的列表理解范围规则是啥? [复制]【英文标题】:What are list comprehension scoping rules within a Python class? [duplicate]Python 类中的列表理解范围规则是什么? [复制] 【发布时间】:2016-11-14 01:48:15 【问题描述】:

在以下代码中,mc 赋值在 Python 2 和 3 中运行良好。

cc 赋值在类中使用相同的列表推导,在 Python 2 中有效,但在 Python 3 中失败。

什么解释了这种行为?

ml1 = "a b c".split()
ml2 = "1 2 3".split()
mc = [ i1 + i2 for i1 in ml1 for i2 in ml2 ]

class Foo(object):
    cl1 = ml1
    cl2 = ml2

    cc1 = [ i1 for i1 in cl1 ]
    cc2 = [ i2 for i2 in cl2 ]
    cc = [ i1 + i2 for i1 in cl1 for i2 in cl2 ]


print("mc = ", mc)
foo = Foo()
print("cc = ", foo.cc)

我明白了:

(default-3.5) snafu$ python2 /tmp/z.py 
('mc = ', ['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3'])
('cc = ', ['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3'])

(default-3.5) snafu$ python3 /tmp/z.py 
Traceback (most recent call last):
  File "/tmp/z.py", line 5, in <module>
    class Foo(object):
  File "/tmp/z.py", line 11, in Foo
    cc = [ i1 + i2 for i1 in cl1 for i2 in cl2 ]
  File "/tmp/z.py", line 11, in <listcomp>
    cc = [ i1 + i2 for i1 in cl1 for i2 in cl2 ]
NameError: name 'cl2' is not defined

为什么没有定义类变量cl2?请注意,cc2 分配工作正常,cc1 也是如此。在理解中交换cl1cl2 表明第二个循环是触发异常的循环,而不是cl2 本身。)

版本:

(default-3.5) snafu$ python2 --version
Python 2.7.11+
(default-3.5) snafu$ python3 --version
Python 3.5.1+

【问题讨论】:

这与类变量与实例变量有关,与理解范围无关。 @TigerhawkT3:实际上,这确实是关于类级理解中的变量范围。 ***.com/questions/20136955/… 【参考方案1】:

在 Python 3 中,列表推导式有自己的作用域,它遵循与函数作用域相同的规则。你知道类的方法不会自动在类范围内查找变量吗?

class Example:
    var = 1
    def this_fails(self):
        print(var)
Example().this_fails()  # NameError

这同样适用于嵌套在类范围内的任何函数范围,包括列表推导的范围。在列表推导中查找cl2 会绕过类范围并直接进入全局变量。它有效地像这样工作:

class Foo(object):
    ...
    def make_cc(outer_iterable):
        result = []
        for i1 in outer_iterable:
            for i2 in cl2:  # This fails
                result.append(i1 + i2)
        return result
    cc = make_cc(cl1)  # cl1 is evaluated outside the comprehension scope, for reasons

请注意,cl1 查找工作正常,因为它发生在类范围内,在理解之外,尽管在语法上嵌套在理解内。 They made that decision 回到 Python 引入 genexps 的时候,因为它更早地捕获了一些常见的 genexp 错误。这也是cc1cc2 列表推导起作用的原因;它们对类级变量的唯一用途是在它们的外部(仅)for 可迭代中。

在类语句中使用推导式和生成器表达式是一团糟。它不应该是,但它是。坚持常规循环,或者在类语句之外运行推导式,这样语义会更明显。

【讨论】:

这听起来很合理,但是,它并没有解释(对我来说)为什么 cc1 = [ i1 for i1 in cl1 ] 有效但 cc = [ i1 + i2 for i1 in cl1 for i2 in cl2 ] 无效。范围规则不一样吗? @mhawke:第一个 for 的可迭代对象在理解范围之外进行评估并作为参数传递给匿名函数,很可能与生成器表达式对称,因为它捕获前面的一些常见错误。 “在类语句中使用推导式和生成器表达式是一团糟。”我同意:成功的cc1cc2 分配和失败的cc 分配似乎一反常态地令人惊讶和不直观。 考虑itertools.product。上述理解变为cc = [ i1 + i2 for i1, i2 in itertools.product(cl1, cl2) ],由于上述答案中给出的原因,它确实在类中工作。

以上是关于Python 类中的列表理解范围规则是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

正则表达式中方括号“[]”内的浮点范围是啥意思? [复制]

我需要正确理解 Java 中的抽象是啥? [复制]

Python 列表推导中的“或”是啥意思? [复制]

proguard 规则中的双星号是啥意思?

java中的serialVersionUID是啥,通常在异常类中? [复制]

Python 中的 %% 是啥? [复制]