我们假设我想找到n**2
以查找小于20000000
的所有数字。
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
()
创建一个生成器,为什么它需要大量内存?答案 0 :(得分:53)
正如其他人在评论中指出的那样,range
在Python 2中创建了一个list
。因此,它不是生成器本身耗尽内存,而是{{ 1}}生成器使用:
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
可以大大提高内存使用率,因为它返回了一个惰性迭代。这实际上是内置内存有效的方式来迭代反映您的第三个版本的一系列数字(xrange(20000000)
,start
和stop
的灵活性增加:
step
在Python 3中,x = (i**2 for i in xrange(20000000))
本质上是{2}在Python 2中的含义。
但是,Python 3 range
对象具有Python 2 xrange
没有的一些很好的功能,如range
切片,包含等等。
答案 1 :(得分:1)
1.-必须在内存中创建对象,因此在第二解决方案中,生成器已创建但未计算,但仍有内存,python可能为其计算保留一些内存效率,我们不知道解释器魔法,还注意range
功能创建从0
到{{1}的完整列表实际上,你仍然在内存中构建该列表。
2.-您可以使用itertool.imap:
200000