Python: 迭代器作为表达式时每次都计算吗?
来看这个例子:
>>> 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 个回答
在这种情况下,你是在内存中创建一个列表,然后遍历这个列表里的内容。所以没错,它只计算一次并存储起来。这和下面这个做法没有什么区别:
i for i in [2,4,6,8]:
print(i)
如果你写 iter(i * 2 for i in xrange(1,10)),你会得到一个迭代器,它会在每次迭代时进行计算。
在这个表达式列表中,所有的成员会被计算一次,然后再逐个处理。
在Python 2.x版本中,列表推导式(LC)中使用的变量会“泄露”到外面的作用域里,但因为列表推导式已经计算过了,所以外面只能拿到生成最终结果时用的那个值。
下面是对 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
,可能会节省时间,但在一般情况下(内存紧张或循环可能提前结束的特殊情况除外),列表推导式通常会快一点点。