Python3 列表解析和迭代器的内存占用过程分析

列表解析表达式

先来看一个例子~ Leetcode 171 题
解法很简单:

sum = 0
for i in range(0, len(s):
	sum += 26 ** (len(s) - 1 - i) * (ord(s[i]) - ord("A") + 1)
return sum

那么,这里也可以这么写。

return sum([26 ** (len(s) - 1 - i) * (ord(s[i]) - ord("A") + 1) for i in range(0, len(s))])

下面的简写方法,我们称作 python的列表解析表达式 。酷酷的 o(╥﹏╥)o有没有

再来延伸看一下这么写的好处(内存占用)

上代码:

import timeit
import os
import psutil
# 准备一个5000w 元素的列表 备用
list = range(0, 50000000)

# 计算占用内存
def show_memory_info(hint):
    pid = os.getpid()
    p = psutil.Process(pid)

    info = p.memory_full_info()
    memory = info.uss / 1024. / 1024
    print('{} memory used: {} MB'.format(hint, memory))

# 时间
start1 = timeit.default_timer()
# 1.普通方法遍历元素,append 到新列表
list1 = []
for x in (list):
    if x == 20000000:
        show_memory_info('1')
    if x > 4:
        list1.append(x)
show_memory_info('2')
end1 = timeit.default_timer()
print('Running time: {} Seconds'.format(end1 - start1))

start2 = timeit.default_timer()

show_memory_info('3')
# 2. 生成器生成元素到新列表,注意这里没有赋值到新列表
(x for x in (list) if x > 4)
show_memory_info('4')
end2 = timeit.default_timer()
print('Running time: {} Seconds'.format(end2 - start2))

运行程序,得到的结果如下:

1 memory used: 783.5625 MB
2 memory used: 1945.28125 MB
Running time: 11.129049652 Seconds
3 memory used: 1945.28125 MB
4 memory used: 1945.28125 MB
Running time: 0.09404864300000071 Seconds

关注几点:

  1. 时间上的差别1:普通遍历时间为9秒,而生成器遍历 (没有创建新列表时间为0.09秒),这个原因在我之前的迭代和生产的对比 博客也写过了,是因为只调用了算法

  2. 时间上的差别2: 如果我把 list1.apped 给删除了,也就是普通遍历的append 操作删除了,时间是 6s 左右 ,看来普通遍历确实要比生成器占用更多的内存(废话),时间也更长

  3. 时间上的关注点3:如果我把生成器赋值 list2 = (x for x in (list) if x > 4) ,这段代码的时间是4秒。 可得出,生成器本身生生成元素只是调用了算法,而并未赋值(实际占用内存)

  4. 内存上的关注点: 同3所说,生成器赋值才产生内存占用,故而更快。

思考

语法糖其实是迭代的一个延伸,合理的运用不仅可以使代码变酷,让别人看不懂,也是可以优化程序运行速度和内存占用的。

发布了210 篇原创文章 · 获赞 124 · 访问量 67万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章