为何分两步获取字典前30个键比一步操作更快?
为什么分两步获取字典前30个键更快?
这确实是个挺有意思的细节!让我们一步步拆解这三种实现的执行逻辑,搞清楚背后的原因:
首先先明确一个基础知识点:Python中dict.keys()返回的是字典视图对象(类型为dict_keys),它不是一个已经存储了所有键的列表,而是一个动态反映字典变化的可迭代对象——只有当你遍历它时,才会逐个取出字典中的键。
三种方法的执行流程
我们先回顾你的测试代码:
import time dic = {str(i): i for i in range(10 ** 6)} # 方法1 start_time = time.time() x = list(dic.keys())[0:30] print(time.time() - start_time) # 方法2 start_time = time.time() y = list(dic.keys()) x = y[0:30] print(time.time() - start_time) # 方法3 start_time = time.time() z = dic.keys() y = list(z) x = y[0:30] print(time.time() - start_time)
方法1:list(dic.keys())[0:30]
执行步骤:
- 调用
list()遍历整个字典的100万个键,生成一个包含百万元素的完整列表(这是最耗时的步骤)。 - 对这个大列表执行切片
[0:30],生成一个只包含前30个元素的小列表,赋值给x。 - 此时,那个百万元素的大列表是临时对象——没有任何变量引用它,Python的垃圾回收机制会很快标记它为待回收对象,并在该表达式执行完成后销毁它。而销毁这个大列表的内存开销,被算进了方法1的总耗时里。
方法2:y = list(dic.keys()); x = y[0:30]
执行步骤:
- 同样生成百万元素的完整列表,但这次把它赋值给了变量
y,这个列表有了明确的引用。 - 对
y执行切片生成小列表赋值给x。 - 由于
y还被引用着,Python不会在这段测试时间内销毁它,因此垃圾回收的额外开销没有被计入方法2的耗时,这就是它比方法1快的核心原因。
方法3:z = dic.keys(); y = list(z); x = y[0:30]
和方法2的核心步骤一致,只是多了一步把字典视图赋值给z的操作。这一步虽然开销极小,但累积下来会让总耗时略高于方法2,和方法1的耗时接近。
更高效的优化方案
其实你的三种方法都做了没必要的工作:生成完整的百万元素列表。既然我们只需要前30个键,完全可以只遍历前30个元素,不需要处理剩下的999970个键。用itertools.islice就能实现:
import itertools start_time = time.time() x = list(itertools.islice(dic.keys(), 30)) print(time.time() - start_time)
这种方法的耗时会远低于你测试的三种方法,因为它只需要从字典视图中取出前30个键,直接生成小列表,完全跳过了遍历百万键的步骤。
内容的提问来源于stack exchange,提问作者Diamond
相关产品推荐
相关产品推荐

