本文参考自python生成器
在创建大容量列表时,列表中元素的数量很多,会导致列表占用的内存空间很大。这种情况下,如果能能记录列表中的当前的元素值以及计算下一个元素的算法(类似于迭代器),那么相当于内存中只需要存储一个元素以及一段算法,内存占用率将显著降低。生成器就是python提供的这样一种对象,他也是一种迭代器。本文通过一个自然数列表求和的方法,比较列表和生成器对内存的占用率。
0. 内存统计函数
echo > memory_statistics.txt
while true
do
now_time=$(date "+%Y-%m-%d %H:%M:%S")
memory_usage=`free -h |grep Mem | awk '{print $3}'`
echo -n $now_time >> memory_statistics.txt
echo " $memory_usage" >> memory_statistics.txt
sleep 0.5
done
该函数将会每个0.5秒记录系统内存的使用率
1. 先看一段自然数求和的函数
import datetime
def firstn(n):
num, nums = 0, []
while num < n:
nums.append(num)
num += 1
return nums
if __name__ == "__main__":
print datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
a = firstn(20000000)
print datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
suma = sum(a)
print datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print suma
很简单的函数,先生成一个0到n的列表,然后求和,这段时间内的内存使用率统计如下:
可以看到在2018-08-08 22:41:31到2018-08-08 22:41:34这段时间内,内存急速增加,占用了600M左右内存。同时可见,构造列表花了3秒,sum函数求和用的时间反倒很少。
2. 接下来我们来实现一种迭代器
import datetime
class Firstn(object):
def __init__(self, n):
self.n = n
self.num = 0
self.nums = []
def __iter__(self):
return self
def __next__(self):
return self.next()
def next(self):
if self.num < self.n:
cur, self.num = self.num, self.num + 1
return cur
else:
raise StopIteration()
print datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
firstn = Firstn(20000000)
print datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print sum(firstn)
print datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
这段函数首先构造了一个迭代器,可以这个迭代器保存当前的元素(self.num)和求计算下一个元素的算法(当前元素增加1),我们来看看这个迭代器的性能
我们可以看到内存使用率增加3M,迭代器产生速度远快于列表,但是在求和阶段,花费了9秒的时间,也远多于列表求和。从这里也可以看出,空间和时间不可兼得。