为什么Python要求列表推导中的子句是错误的方法?

时间:2013-04-02 14:29:18

标签: python list list-comprehension

看两个列表推导,每个包含两个用于子句。我们看到如果for子句的顺序正确,Python就会感到困惑。但如果它们是错误的方式,Python可以处理它。为什么呢?

Python 3.2.3 (default, Apr 11 2012, 07:15:24) [MSC v.1500 32 bit (Intel)] on win
32
Type "help", "copyright", "credits" or "license" for more information.
>>> [x + y for x in range(y) for y in range(4)]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> [x + y for y in range(4) for x in range(y)]
[1, 2, 3, 3, 4, 5]
>>>

3 个答案:

答案 0 :(得分:5)

[x + y for x in range(y) for y in range(4)]

y中的range(y)在该地方未知。相当于:

for x in range(y):
    for y in range(4):
        # x + y

有关详细信息,请参阅PEP-0202

- The form [... for x... for y...] nests, with the last index
      varying fastest, just like nested for loops.

答案 1 :(得分:3)

这是一个很好的问题!

答案是,与大多数Python不同,这些嵌套的理解是按照解释器想要的方式编写的,而不是你想要的方式。

口译员从右到左阅读理解:

[(this is interpreted last) for (this is interpreted first)]

但是,每个子句都是从左到右阅读的,因此在y in range(4) for x in range(y)中,您必须先解释y之前的内容,然后才能range(y)

这令人困惑,因为当你想到嵌套理解时,你会从右到左思考。

答案 2 :(得分:3)

列表推导首先被引入Python作为这种形式的语法糖:

L = []
for innerseq in seq:
    for item in innerseq:
        LOOPS
            if CONDITION:
                L.append(BODY)

这转变为:

[BODY for innerseq in seq for item in innerseq LOOPS if CONDITION]

为了使转换更加明显,请注意for表达式和if条件的顺序与正常for - 循环中的顺序完全相同。这就是列表推导使用相同顺序的原因。

当你将循环重写为理解时,唯一改变的是循环体的位置(它移动到前面,你通常会初始化空容器)。关于循环的其他所有内容都保持完全相同。

你喜欢的替代品(你的“正确方式”)似乎更令人困惑。我们只是颠倒循环的顺序,或者我们颠倒理解中每个子句的顺序。即,或者:

[BODY LOOPS[::-1] for item in innerseq for innerseq in seq if CONDITION]

[BODY if CONDITION LOOPS[::-1] for item in innerseq for innerseq in seq]

其中任何一个看起来都是不必要的复杂变换。

另请注意,其他语言在列表推导中使用相同的循环顺序。这是一些Clojure:

user=> ; using your suggested "right" order
user=> (for [x (range y) y (range 4)] (+ x y))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: y in this context, compiling:(NO_SOURCE_PATH:1) 
user=> ; you need to use the same "wrong" order as Python
user=> (for [y (range 4) x (range y)] (+ x y))
(1 2 3 3 4 5)

这与Python相同,尽管Clojure最后将理解的“主体”放在了一起。

如果有帮助,可以想象for循环的排列方式与汽车里程表中的数字相同。最右边的循环旋转速度最快。