问题
假设我想为小于 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
问题是:
- 第一种和第二种解决方案有什么区别?使用
()
会创建一个生成器,那么为什么它需要大量内存呢? - 是否有与我的第三个选项等效的内置函数?
最佳答案
正如其他人在评论中指出的那样,
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
这也解释了为什么您的第二个版本(生成器表达式)使用了第一个版本(列表推导式)大约一半的内存,因为第一个版本构建了两个列表(用于底数和正方形),而第二个仅构建了一个碱基列表。
xrange(20000000)
因此,当它返回一个惰性迭代时,极大地提高了内存使用率。这本质上是一种内置内存高效的方式来迭代一系列反射(reflect)您的第三个版本的数字(增加了start
、stop
和step 的灵 active
):x = (i**2 for i in xrange(20000000))
在 Python 3 中,
range
本质上与 Python 2 中的xrange
相同。 但是,Python 3range
对象具有 Python 2 的xrange
所没有的一些不错的功能,例如O(1)
切片,包含,等等。
一些引用资料:
关于python - 为什么生成器表达式需要大量内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37156574/