3

看看两个列表推导,每个都包含两个 for 子句。我们看到,如果 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]
>>>
4

3 回答 3

5

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

在那个地方是未知的yrange(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.
于 2013-04-02T14:30:45.897 回答
3

这是一个很好的问题!

答案是,与大多数 Python 不同,这些嵌套推导是按照解释器想要读取它们的方式编写的,而不是您想要编写它们的方式。

解释器从右到左阅读理解:

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

但是,每个子句都是从左到右阅读的,因此y in range(4) for x in range(y)您必须先解释一下y是什么,然后才能range(y)摆脱它。

这很令人困惑,因为当您考虑嵌套理解时,您会认为是从右到左。

于 2013-04-02T14:32:13.133 回答
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回路的排列方式就像汽车里程表中的数字一样。最右边的循环旋转最快。

于 2013-04-02T14:41:17.957 回答