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
也是如此。在理解中交换cl1
和cl2
表明第二个循环是触发异常的循环,而不是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 错误。这也是cc1
和cc2
列表推导起作用的原因;它们对类级变量的唯一用途是在它们的外部(仅)for
可迭代中。
在类语句中使用推导式和生成器表达式是一团糟。它不应该是,但它是。坚持常规循环,或者在类语句之外运行推导式,这样语义会更明显。
【讨论】:
这听起来很合理,但是,它并没有解释(对我来说)为什么cc1 = [ i1 for i1 in cl1 ]
有效但 cc = [ i1 + i2 for i1 in cl1 for i2 in cl2 ]
无效。范围规则不一样吗?
@mhawke:第一个 for
的可迭代对象在理解范围之外进行评估并作为参数传递给匿名函数,很可能与生成器表达式对称,因为它捕获前面的一些常见错误。
“在类语句中使用推导式和生成器表达式是一团糟。”我同意:成功的cc1
和cc2
分配和失败的cc
分配似乎一反常态地令人惊讶和不直观。
考虑itertools.product
。上述理解变为cc = [ i1 + i2 for i1, i2 in itertools.product(cl1, cl2) ]
,由于上述答案中给出的原因,它确实在类中工作。以上是关于Python 类中的列表理解范围规则是啥? [复制]的主要内容,如果未能解决你的问题,请参考以下文章