为什么`dict in dict`和`key in dict.keys()`有相同的输出?

时间:2015-03-28 06:10:04

标签: python dictionary

我试图在词典中搜索键,但我忘了添加keys()功能。我仍然得到了预期的答案。

为什么这两个表达式的结果相同?

key in dict

key in dict.keys()

2 个答案:

答案 0 :(得分:23)

要理解为什么key in dct返回与key in dct.keys()相同的结果,需要查看过去。从历史上看,在Python 2中,人们会使用dct.has_key(key)在字典dct中测试的存在。当Python 2.2的首选方式变为key in dct时,Python language reference - 3 Data Model的情况发生了变化,这基本上做了同样的事情:

  

在次要相关更改中,in运算符现在适用于词典,因此key in dict现在等同于dict.has_key(key)

in的行为是根据__contains__ dunder方法在内部实施的。它的行为记录在Python 3

  

object.__contains__(self, item)

     

被调用以实现成员资格测试运营商。如果item在self,则返回true,否则返回false。 对于映射对象,应考虑映射的键而不是值或键 - 项对。   对于未定义__contains__()的对象,成员资格测试首先通过__iter__()尝试迭代,然后通过__getitem__()尝试旧的序列迭代协议,请参阅语言参考中的此部分。

(强调我的; Python中的字典是映射对象)

view中,has_key方法已完全删除,现在 正确的方法来测试密钥的存在仅为key in dict,如记录。


与上面的2相反,key in dct.keys() 从来没有正确的方式来测试字典中是否存在密钥。 你的两个例子的结果确实是一样的,但是{3}在Python 3上稍微慢一些,而且在Python 2上非常慢。

key in dct.keys()返回true,如果key in dct在几乎恒定的时间操作中被发现为key中的一个键 - 那么是否有两个或一百万个键无关紧要 - 时间复杂度在平均情况下是恒定的( O(1)

Python 2中的

dct创建了所有键的dct.keys();并在Python 3中key in key_list个键;这两个对象都理解list。使用Python 2,它适用于任何 iterable ;迭代值,只要一个值等于给定值(此处为key in x),就会返回True

实际上,在Python 2中,您发现keykey in dct.keys()慢得多(key in dict与键数呈线性关系 - 其时间复杂度为 O(n ) - key in dct.keys(),它构建了所有键的列表,而view O(n)

在Python 3中,dct.keys()不会比key in dct.keys()慢得多,因为{{3}}没有列出密钥,访问仍然是 O(1),但实际上它会慢到至少一个常数值,并且它还有7个字符,因此通常没有理由使用它,即使在Python 3上也是如此。

答案 1 :(得分:4)

Python data model规定通常会员测试通常作为序列的迭代实现,除非容器对象提供特殊方法 __contains__

正如文档中进一步提到的,对于未实现 __contains__ 特殊方法的对象,成员资格测试首先通过__iter__()尝试迭代,然后通过__getitem__()尝试旧的序列迭代协议

重要的是要知道对于词典,dict.keys()返回或者是一个迭代器字典视图(Python 3.X)或序列(更准确地说)列表),在 Python(2.X)中。序列/列表的成员资格测试是O(n)复杂度,对于像对象这样实现为哈希映射的字典,或支持像支持成员测试和迭代等操作的操作的字典视图具有O的复杂性(1)。

对于Python 2.X来说,两者的区别在于它们可能会影响性能,而对于Python 2.X,唯一的开销是额外的函数调用。

在任何情况下,始终首选使用dict对象上的成员资格,而不是在字典视图或dict.keys返回的序列上使用成员资格测试