python - 为什么生成器表达式需要大量内存?

标签 python python-2.7 generator

问题

假设我想为小于 20000000 的所有数字找到 n**2

我测试的所有三个变体的常规设置:

import time, psutil, gc

gc.collect()
mem_before = psutil.virtual_memory()[3]
time1 = time.time()

# (comprehension, generator, function)-code comes here

time2 = time.time()
mem_after =  psutil.virtual_memory()[3]

print "Used Mem = ", (mem_after - mem_before)/(1024**2)  # convert Byte to Megabyte
print "Calculation time = ", time2 - time1

计算这些数字的三个选项:

1.创建通过理解的列表:

x = [i**2 for i in range(20000000)]

真的很慢很耗时:

Used Mem =  1270  # Megabytes
Calculation time =  33.9309999943  # Seconds

<强>2。使用 '()' 创建生成器:

x = (i**2 for i in range(20000000))

它比选项 1 快得多,但仍然占用大量内存:

Used Mem =  611 
Calculation time =  0.278000116348 

3.定义生成器函数(最有效):

def f(n):
    i = 0
    while i < n:
        yield i**2
        i += 1
x = f(20000000)

它的消耗:

Used Mem =  0
Calculation time =  0.0

问题是:

  1. 第一种和第二种解决方案有什么区别?使用 () 会创建一个生成器,那么为什么它需要大量内存呢?
  2. 是否有与我的第三个选项等效的内置函数?

最佳答案

  1. 正如其他人在评论中指出的那样,range 在 Python 2 中创建了一个 list。因此,使用内存,但是生成器使用的 range:

    x = (i**2 for i in range(20000000))  
    # builds a 2*10**7 element list, not for the squares , but for the bases
    
    >>> sys.getsizeof(range(100))
    872
    >>> sys.getsizeof(xrange(100))
    40
    >>> sys.getsizeof(range(1000))
    8720
    >>> sys.getsizeof(xrange(1000))
    40
    >>> sys.getsizeof(range(20000000))
    160000072
    >>> sys.getsizeof(xrange(20000000))
    40
    

    这也解释了为什么您的第二个版本(生成器表达式)使用了第一个版本(列表推导式)大约一半的内存,因为第一个版本构建了两个列表(用于底数和正方形),而第二个仅构建了一个碱基列表。

  2. xrange(20000000) 因此,当它返回一个惰性迭代时,极大地提高了内存使用率。这本质上是一种内置内存高效的方式来迭代一系列反射(reflect)您的第三个版本的数字(增加了 startstopstep 的灵 active ):

    x = (i**2 for i in xrange(20000000))
    

    在 Python 3 中,range 本质上与 Python 2 中的 xrange 相同。 但是,Python 3 range 对象具有 Python 2 的 xrange 所没有的一些不错的功能,例如 O(1) 切片,包含,等等。

一些引用资料:

关于python - 为什么生成器表达式需要大量内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37156574/

相关文章:

python - 如何在 Matplotlib 中为多个图形设置静态 Y 轴?

python - 按变量 pandas 过滤

python - 是否可以在 lambda 函数被终止并冷启动之前拦截终止信号以关闭数据库连接?

java - 有没有开源的完整的Java EE MDA框架?

python - 使用 Python 导入 o​​pencv

python - Microsoft Visual Studio 2013 中没有名为 Flask 的模块

python - 如何将 ResultProxy 更改为字符串

python-2.7 - python + wx & uno 使用 ubuntu 14.04 填充 libreoffice

c++ - 为什么随机数生成器的位数有限制?

python - 如何解压缩迭代器?