Python 字典的键。“在”复杂性中

2025-03-20 08:47:00
admin
原创
45
摘要:问题描述:问一个简单的问题,主要是为了满足我对这个话题的好奇心。我正在使用 SQlite 数据库后端编写一些大型 python 程序,将来会处理大量记录,因此我需要尽可能地进行优化。对于一些函数,我正在字典中搜索键。我一直在使用“in”关键字进行原型设计,并计划稍后返回并优化这些搜索,因为我知道“in”关键字...

问题描述:

问一个简单的问题,主要是为了满足我对这个话题的好奇心。

我正在使用 SQlite 数据库后端编写一些大型 python 程序,将来会处理大量记录,因此我需要尽可能地进行优化。

对于一些函数,我正在字典中搜索键。我一直在使用“in”关键字进行原型设计,并计划稍后返回并优化这些搜索,因为我知道“in”关键字通常是 O(n)(因为这只会转化为 python 遍历整个列表并比较每个元素)。但是,由于 python 字典基本上只是一个哈希映射,python 解释器是否足够智能来解释:

if(key in dict.keys()):
    ...code...

到:

if(dict[key] != None):
    ...code...

它基本上是相同的操作,但顶部为 O(n),底部为 O(1)。

我可以轻松地在我的代码中使用底部版本,但我只是好奇并想问一下。


解决方案 1:

首先,key in d.keys()保证给你key in d与任何 dict相同的值d

in对 的操作,dict或者对它dict_keys调用后返回的对象(在 3.x 中)不是O(N),而是 O(1)。keys()

并没有进行真正的“优化”;只是使用哈希是__contains__在哈希表上实现的明显方法,就像它是实现的明显方法一样__getitem__


您可能会问哪里能保证这一点。

嗯,不是。映射类型基本上定义dict为的哈希表实现collections.abc.Mapping。没有什么可以阻止某人创建映射的哈希表实现,但仍提供 O(N) 搜索。但是,制作如此糟糕的实现需要额外的工作,那么他们为什么要这样做呢?

如果您真的需要向自己证明这一点,您可以测试您关心的每个实现(使用分析器,或使用某种自定义类型__hash____eq__记录调用,或......),或阅读源代码。


在 2.x 中,您不想调用keys,因为这会生成一个list键,而不是KeysView。您可以使用iterkeys,但这可能会生成迭代器或其他非 O(1) 的东西。因此,只需将字典本身用作序列即可。

即使在 3.x 中,您也不想调用keys,因为没有必要。迭代dict,检查其__contains__,并且通常将其视为序列,始终等同于对其键执行相同的操作,所以为什么要费心呢?(当然,构建简单的KeyView,并通过它进行访问,将使您的运行时间增加几纳秒,并为您的程序增加一些击键次数。)

(目前还不太清楚在 2.x 中使用序列操作是否等同于d.keys()/d.iterkeys()d。除了性能问题之外,它们在每个 CPython、Jython、IronPython 和 PyPy 实现中都是等效的,但似乎没有在任何地方像在 3.x 中那样说明。这没关系;只需使用key in d。)


当我们这样做时,请注意以下几点:

if(dict[key] != None):

… 不起作用。如果key不在 中dict,这将引发KeyError,而不是返回None

此外,您永远不应该None使用==或进行检查!=;而应该始终使用is

您可以使用try— 来执行此操作,或者更简单地执行if dict.get(key, None) is not None。但同样,没有理由这样做。此外,这不会处理 是None完全有效项目的情况。如果是这种情况,您需要执行类似 的操作sentinel = object(); if dict.get(key, sentinel) is not sentinel:


因此,正确的写法是:

if key in d:

更一般地来说,这是不正确的:

我知道“in”关键字通常是 O(n) (因为这只意味着 python 遍历整个列表并比较每个元素

in运算符与大多数其他运算符一样,只是对方法的调用__contains__(或相当于 C/Java/.NET/RPython 内置函数)。list通过迭代列表并比较每个元素来实现它;dict通过对值进行哈希处理并查找哈希来实现它;blist.blist通过遍历 B+Tree 来实现它;等等。因此,它可以是 O(n)、O(1)、O(log n) 或完全不同的东西。

解决方案 2:

在 Python 2 中,dict.keys()首先创建整个键列表,这就是为什么它是一个O(N)操作,而key in dict是一个O(1)操作。

if(dict[key] != None)`KeyError`如果在字典中找不到键,则会引发,因此它不等同于第一个代码。

Python 2 结果:

>>> dic = dict.fromkeys(range(10**5))
>>> %timeit 10000 in dic
1000000 loops, best of 3: 170 ns per loop
>>> %timeit 10000 in dic.keys()
100 loops, best of 3: 4.98 ms per loop
>>> %timeit 10000 in dic.iterkeys()
1000 loops, best of 3: 402 us per loop
>>> %timeit 10000 in dic.viewkeys()
1000000 loops, best of 3: 457 ns per loop

在 Python 3 中dict.keys()返回一个视图对象,它比 Python 2 的速度要快得多,keys()但仍然比简单的正常速度慢key in dict

Python 3 结果:

>>> dic = dict.fromkeys(range(10**5))
>>> %timeit 10000 in dic
1000000 loops, best of 3: 295 ns per loop
>>> %timeit 10000 in dic.keys()
1000000 loops, best of 3: 475 ns per loop

仅使用:

if key in dict:
   #code

解决方案 3:

正确的做法是

if key in dict:
    do stuff

对于 Python 中的字典和集合, in运算符是 O(1)。

解决方案 4:

dict 的 in 运算符的平均时间复杂度为 O(1)。有关其他 dict() 方法的时间复杂度的详细信息,请访问此链接。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2482  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1533  
  PLM(产品生命周期管理)项目对于企业优化产品研发流程、提升产品质量以及增强市场竞争力具有至关重要的意义。然而,在项目推进过程中,范围蔓延是一个常见且棘手的问题,它可能导致项目进度延迟、成本超支以及质量下降等一系列不良后果。因此,有效避免PLM项目范围蔓延成为项目成功的关键因素之一。以下将详细阐述三大管控策略,助力企业...
plm系统   0  
  PLM(产品生命周期管理)项目管理在企业产品研发与管理过程中扮演着至关重要的角色。随着市场竞争的加剧和产品复杂度的提升,PLM项目面临着诸多风险。准确量化风险优先级并采取有效措施应对,是确保项目成功的关键。五维评估矩阵作为一种有效的风险评估工具,能帮助项目管理者全面、系统地评估风险,为决策提供有力支持。五维评估矩阵概述...
免费plm软件   0  
  引言PLM(产品生命周期管理)开发流程对于企业产品的全生命周期管控至关重要。它涵盖了从产品概念设计到退役的各个阶段,直接影响着产品质量、开发周期以及企业的市场竞争力。在当今快速发展的科技环境下,客户对产品质量的要求日益提高,市场竞争也愈发激烈,这就使得优化PLM开发流程成为企业的必然选择。缺陷管理工具和六西格玛方法作为...
plm产品全生命周期管理   0  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用