首先,对于任何字典 d,在调用 d.keys()
和使用 key in d
进行判断时,返回的结果是相同的。
在字典或者字典对象通过调用 keys()
返回的 dict_keys
对象上执行 in
操作(在 3.x 版本中),时间复杂度不是 O(N),而是 O(1)。
并没有真正的“优化”操作;在哈希表上实现 __contains__
的明显方式就是使用哈希,正如实现 __getitem__
的明显方式一样。
你可能会问这是怎么保证的。
事实上并没有保证。在 Mapping Types 中,dict
被定义为基于 collections.abc.Mapping
实现的哈希表。虽然也可以创建一个哈希表实现的 Mapping,但是提供 O(N) 搜索需要额外的工作,那么为什么要这样做呢?
如果你真的需要验证这个结论,你可以测试每个你关心的实现(使用分析器或者使用自定义的 __hash__
和 __eq__
记录函数调用次数,等等),或者阅读源代码。
在 2.x 版本中,你不应该调用 keys
,因为这将生成一个键的列表而不是 KeysView
。你可以使用 iterkeys
,但是它可能会生成一个迭代器或其他不是 O(1) 的东西。所以,只需将字典本身作为序列使用。
即使在 3.x 版本中,也没有必要调用 keys
。遍历字典、检查其 __contains__
,并一般地把它当作序列来对待,总是等价于对其键做同样的事情,那么为什么还要麻烦呢?(当然,构建简单的 KeyView
,并通过它进行访问,会增加几个纳秒的运行时间和一些程序代码。)
(虽然使用序列操作在 2.x 中的 d.keys()
/d.iterkeys()
和 d
等价于性能问题,但它们在每个 CPython、Jython、IronPython 和 PyPy 实现中都是等价的,但似乎没有像在 3.x 中那样声明。并且这也无关紧要;只需使用 key in d
即可。)
顺带一提,这个式子:
if(dict[key] != None):
如果在字典中没有找到 key
,这样写会抛出 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+树 来实现它;等等。所以,它可能是 O(n)、O(1)、O(log n),或者完全不同。
try:dict [key];except KeyError:pass;else:#...code ...
的操作。 - Travis DePratoif
条件用括号括起来。熟练阅读和编写Python代码的人会认为括号代表某些意义,如元组、生成器表达式或者覆盖运算符优先级,他们会不得不停下来读两遍以确保您的括号实际上并没有表示任何东西。 简化翻译:不必要地给if
语句加括号会让人感到困惑,因为读写Python的有经验的人会认为括号代表了一些含义,这可能导致需要反复阅读才能理解代码的真正含义。 - abarnertdict
,因为这会隐藏同名类型和构造函数,而你在后面可能还会用到它们。 - abarnert