在 Python 3 里,range 这个东西啊,说起来是个“看着普通,用着精妙”的代表。尤其是在内存占用这块,可以说是“极致克制”的典范。
我们先说一个很直观的现象:是不是有点神奇?你创建了一个从0到999999的超长范围,它居然和只创建0到9的对象占用一样的内存。为啥?因为 range 根本就不存储每一个数值,它只是存了几个控制变量——开始、结束、步长。
说白了,它就是一个“假装是个列表”的对象,它没把所有的元素都放进内存里,而是到了你用的时候,才实时计算出当前应该给你哪个值。
就像你告诉它:“我想从 0 数到 10,步长是 1”,它只会把这三个数:start=0, stop=10, step=1,记在自己的脑子里。剩下的你什么时候要,我就现算一个给你。CPU干这个活比内存省事多了,而且效率还贼高。
你可能会问:那这样计算出来的值是不是慢呢?答案是:几乎感觉不到。我们看下源码实现(简化版的意思):
每次你访问 range[i],它就这么一算,瞬间就拿到了结果。不是列表那种一项项放在内存里的方式,而是动态计算。因为只用了加法和乘法这种极快的整数运算,所以性能可以说是稳得一批。
这就是为什么在你用 for i in range(1000000000): 的时候,Python 照样跑得飞快,而你系统的内存也不会炸。因为它根本就没把那10亿个数字都放在内存里。
再来看看极端对比:
我在之前搞爬虫数据清洗的时候,就吃过这亏。用了 list(range(n)) 处理分批任务,结果在高并发场景下,几个线程加一起瞬间爆内存... 后来全都改成了 range(n) 原始对象,用的时候才转成迭代器取数据,瞬间稳住。
还有个细节值得一提:虽然 range 很省内存,但它并不是“懒加载到极致”。它内部其实有缓存机制,比如你频繁访问某些 index,它会在内部保存一段范围,免得每次都重新算。当然这块 Python 不太会暴露给用户,但确实有助于提升性能。
再多说一点偏门但实用的点:
你知道 range() 返回的对象可以比列表更适合做集合操作吗?因为 range 是不可变的(immutable),可以当字典的 key 或放到 set 里,而列表就不行:
有时候你需要在做缓存、去重操作的时候,就可以用 range 当做一个“轻量级不可变区间标识符”。
所以总结一下,range() 的内存占用方式就是:
只记录起始值、终止值、步长三个整数
不存储任何实际数据
访问时即时计算,不需要预先加载
所有实例的大小几乎相同,极小的内存 footprint
是 immutable 的,可以安全地当字典 key 或放入集合
你要是现在还在处理几百万个任务,用的是 list(range(...)),那我劝你赶紧改,range 本身就够用了。
最后来个面试高分回答模板:
面试最优回答:
Python 3 中的 range() 返回的是一个轻量级的、不可变的可迭代对象,它只存储起始值、结束值和步长,所有元素在访问时才动态计算,因此非常节省内存。无论生成多大范围的数列,range 对象本身的内存占用几乎恒定,是处理大规模数据遍历时非常推荐的方式。如果需要迭代取值,可以通过 iter(range()) 转为迭代器使用,这种惰性计算+固定内存的设计是 Python 在性能优化上的一个亮点。
扫码二维码 获取免费视频学习资料
- 本文固定链接: http://phpxs.com/post/13126/
- 转载请注明:转载必须在正文中标注并保留原文链接
- 扫码: 扫上方二维码获取免费视频资料
查 看2022高级编程视频教程免费获取