Python: 迭代器作为表达式时每次都计算吗?

7 投票
3 回答
676 浏览
提问于 2025-04-16 00:33

来看这个例子:

>>> for item in [i * 2 for i in range(1, 10)]:
    print item


2
4
6
8
10
12
14
16
18

问一下,[i * 2 for i in range(1, 10)]这个表达式是在每次循环的时候都计算一次,还是只计算一次然后存起来?(另外,这个表达式的那部分叫什么名字呢?)

我想这样做的一个原因是,我只希望这个列表推导的结果在循环内部可用。

3 个回答

3

在这种情况下,你是在内存中创建一个列表,然后遍历这个列表里的内容。所以没错,它只计算一次并存储起来。这和下面这个做法没有什么区别:

i for i in [2,4,6,8]:
   print(i)

如果你写 iter(i * 2 for i in xrange(1,10)),你会得到一个迭代器,它会在每次迭代时进行计算。

4

在这个表达式列表中,所有的成员会被计算一次,然后再逐个处理。

在Python 2.x版本中,列表推导式(LC)中使用的变量会“泄露”到外面的作用域里,但因为列表推导式已经计算过了,所以外面只能拿到生成最终结果时用的那个值。

8

下面是对 for i in <whatever>: <loopbody> 的简单解释,能清楚地说明它对任何 <whatever>任何 <loopbody> 的作用:

_aux = iter(<whatever>)
while True:
  try: i = next(_aux)
  except StopIteration: break
  <loopbody>

不过,这里我用的伪变量 _aux 实际上是没有名字的。

所以,<whatever> 只会被计算一次(为了得到一个 iter()),然后生成的迭代器会一直用 next 来获取下一个值,直到没有值可取为止(除非在 <loopbody> 中有 break 的情况)。

在你使用的列表推导式中,计算会生成一个 list 对象(在你的代码示例中没有名字)。在下面这段非常相似的代码中:

for item in (i * 2 for i in range(1, 10)): ...

使用生成表达式而不是列表推导式(语法上用圆括号代替列表推导式的方括号),实际工作主要是由 next() 来完成(它会推进 i 并将其翻倍),而不是在构造时就把所有工作都做完——这样会占用更少的临时内存,如果循环体很可能会提前 break,可能会节省时间,但在一般情况下(内存紧张或循环可能提前结束的特殊情况除外),列表推导式通常会快一点点。

撰写回答