我试图在词典中搜索键,但我忘了添加keys()
功能。我仍然得到了预期的答案。
为什么这两个表达式的结果相同?
key in dict
和
key in dict.keys()
答案 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))
dct
创建了所有键的dct.keys()
;并在Python 3中key in key_list
个键;这两个对象都理解list
。使用Python 2,它适用于任何 iterable ;迭代值,只要一个值等于给定值(此处为key in x
),就会返回True
。
实际上,在Python 2中,您发现key
比key 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
返回的序列上使用成员资格测试