为什么不同方式创建的Python列表__sizeof__()返回值不同,元组却一致?
结论
列表的创建方式确实会影响__sizeof__()的计算结果,该差异来自CPython中列表的动态数组过度分配机制;而元组作为不可变类型不存在该特性,因此不同创建方式返回的大小一致。
核心前提说明
__sizeof__()方法返回的是Python对象自身占用的内存字节数,不统计对象内部引用的其他元素的内存开销。
列表大小差异原因
列表本质是动态数组,为了减少频繁新增元素带来的内存重新申请、数据拷贝开销,CPython会在列表扩容时额外分配一部分预留空间(过度分配),不同创建方式能提前获取的元素总数信息不同,因此过度分配的程度不一样:
- 字面量直接创建:Python解析字面量时可以直接确定列表长度为10,不会额外分配预留空间,仅分配存储10个元素指针所需的最小空间,因此内存占用最小,测试结果为120字节。
- 生成器转列表:传入list构造器的生成器是惰性迭代对象,构造器无法提前预知最终元素总数,只能边迭代边按照动态扩容规则增加空间,迭代完成后剩余的预留空间比字面量创建的列表多,因此测试结果为136字节。
- 列表推导式创建:列表推导式在CPython实现中会先获取可迭代对象(此处为range(10))的长度做预分配,同时采用了更激进的过度分配策略,因此预留的空间最多,测试结果为168字节。
注意:上述测试的字节数值仅对应特定版本、特定架构的CPython实现,不同环境下数值会有区别,但大小差异的逻辑是通用的。
元组大小一致原因
元组是不可变类型,一旦创建完成就不支持新增、删除元素,不需要预留扩容空间。不管用什么方式创建,只要最终包含的元素数量相同,就只会分配刚好够用的内存,因此两种创建方式返回的__sizeof__()结果完全一致。
你提供的测试代码验证了上述逻辑:
# 列表演示 >>> l1 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> print(l1.__sizeof__()) 120 >>> l2 = list(i for i in range(10)) >>> print(l2.__sizeof__()) 136 >>> l3 = [i for i in range(10)] >>> print(l3.__sizeof__()) 168 # 元组演示 >>> t1 = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) >>> print(t1.__sizeof__()) 104 >>> >>> t2 = tuple(i for i in range(10)) >>> print(t2.__sizeof__()) 104
内容的提问来源于stack exchange,提问作者sumixam
相关产品推荐
相关产品推荐

