迭代器是否在Python中节省内存?

时间:2016-02-01 13:24:47

标签: python memory

我不太了解迭代器如何在Python中有内存。

>>> l1 = [1, 2, 3, 4, 5, 6]
>>> l2 = [2, 3, 4, 5, 6, 7]
>>> iz = izip(l1, l2)

我们仍然需要O(min(l1, l2))内存,因为我们需要在内存中加载列表l1l2

我认为迭代器的一个主要用途是节省内存 - 但它似乎没有用处。

同样,下面的代码对我来说还不清楚:

>>> l1 = ( n for n in [1, 2, 3, 4, 5, 6] )
>>> l2 = ( n for n in [2, 3, 4, 5, 6, 7] )
>>> iz = izip(l1, l2)

我们需要在将列表转换为生成器之前加载列表,对吧?这意味着我们会浪费记忆力。那么 - 这里的发电机也是如此。

这是唯一对我有意义的案例:

def build_l1():
    for n in xrange(1, 6):
       yield n

def build_l2:
    for n in xrange(2, 7):
       yield n

l1 = build_l1()
l2 = build_l2()
iz = izip(l1, l2)

没有任何数组被加载到内存中。因此,我们在O(1)记忆中。

Python中迭代器函数的内存使用情况如何?前两种情况似乎使用O(min(l1, l2))内存。我认为迭代器的要点是节省内存,这使得前两种情况看起来毫无用处。

2 个答案:

答案 0 :(得分:5)

您的示例太简单了。考虑一下:

nums = [1, 2, 3, 4, 5, 6]
nums_it = (n for n in nums)

nums_it是一个生成器,它返回nums未修改的所有项目。显然你没有任何优势。但请考虑一下:

squares_it = (n ** 2 for n in nums)

并将其与:

进行比较
squares_lst = [n ** 2 for n in nums]

使用squares_it时,我们只会在请求时动态生成nums的方块。使用squares_lst,我们会立即生成所有这些内容,并将它们存储在新列表中。

所以,当你这样做时:

for n in squares_it:
    print(n)

就像你在做的那样:

for n in nums:
    print(n ** 2)

但是当你这样做时:

for n in squares_lst:
    print(n)

就像你在做的那样:

squares_lst = []
for n in nums:
    squares_lst.append(n ** 2)
for n in squares_lst:
    print(n)

如果您不需要(或没有)列表nums,那么您可以使用以下方法节省更多空间:

squares_it = (n ** 2 for n in xrange(1, 7))

生成器和迭代器也提供了另一个显着优势(实际上可能是一个缺点,具体取决于具体情况):它们被懒惰地评估。

此外,生成器和迭代器可能会产生无限数量的元素。一个例子是itertools.count(),它产生0,1,2,3 ......永不停止。

答案 1 :(得分:0)

>>> l1 = [1, 2, 3, 4, 5, 6]
>>> l2 = [2, 3, 4, 5, 6, 7]
>>> iz = izip(l1, l2)
  

我们仍然需要O(min(l1,l2))内存,因为我们需要将列表l1和l2加载到内存中。

使用zip,您需要存储两个原始列表和压缩列表。使用izip时,您不会存储压缩列表。

如果您必须使用真实的物理机器而不是机器的某些抽象概念,那么Big O符号在这里并不是特别有用。你的O(n)计算中有一个隐藏的常数乘数,它可能在n趋于无穷大之前很好地影响代码的实用性。

>>> l1 = ( n for n in [1, 2, 3, 4, 5, 6] )
>>> l2 = ( n for n in [2, 3, 4, 5, 6, 7] )
>>> iz = izip(l1, l2)
  

我们需要在将列表转换为生成器之前加载列表,对吧?这意味着我们会浪费记忆力。那么 - 这里的生成器也是如此。

这里没有发电机。每当您在n for n in <expr>for过滤器之后看到if <expr>没有更复杂的表达式时,这就是代码气味,因为您可以直接使用原始序列。只有将输入值转换为其他值或过滤它们时,生成器才会变得有用。