我不太了解迭代器如何在Python中有内存。
>>> l1 = [1, 2, 3, 4, 5, 6]
>>> l2 = [2, 3, 4, 5, 6, 7]
>>> iz = izip(l1, l2)
我们仍然需要O(min(l1, l2))
内存,因为我们需要在内存中加载列表l1
和l2
。
我认为迭代器的一个主要用途是节省内存 - 但它似乎没有用处。
同样,下面的代码对我来说还不清楚:
>>> 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))
内存。我认为迭代器的要点是节省内存,这使得前两种情况看起来毫无用处。
答案 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>
没有更复杂的表达式时,这就是代码气味,因为您可以直接使用原始序列。只有将输入值转换为其他值或过滤它们时,生成器才会变得有用。