我应该在Python字典上使用'has_key()'或'in'吗?


910

我不知道该怎么办:

d = {'a': 1, 'b': 2}
'a' in d
True

要么:

d = {'a': 1, 'b': 2}
d.has_key('a')
True

Answers:


1286

in 绝对更pythonic。

实际上has_key()已在Python 3.x中删除


3
另外,在Python 3中,要检查值(而不是键)中是否存在,请在d.values()中尝试>>> 1
riza

217
但要避免的一个半难题是确保您做到了:“键入some_dict”而不是“键入some_dict.keys()”。两者在语义上是等效的,但在性能方面后者则要慢得多(O(n)vs O(1))。我见过人们在做“ in dict.keys()”时认为它更明确,因此更好。
亚当·帕金

2
@AdamParkin我已在我的答案中展示了您的评论stackoverflow.com/a/41390975/117471
Bruno Bronosky 2016年

8
@AdamParkin在Python 3中,keys()只是字典中的类似集合的视图,而不是副本,x in d.keys()O(1)也是如此。尽管如此,x in d还是更加Pythonic。
亚瑟·塔卡

2
@AdamParkin有趣的是,我没有看到。我想这是因为x in d.keys()必须构造和销毁一个临时对象,并完成所需的内存分配,在这里x in d.keys()只是进行算术运算(计算哈希)并进行查找。请注意,d.keys()它只是此长度的10倍左右,实际上还不是很长。我没有检查,但我仍然很确定它只是O(1)。
亚瑟·塔卡

253

in 不仅在优雅方面(而且不被弃用;-),而且在性能方面,都赢得了放手,例如:

$ python -mtimeit -s'd=dict.fromkeys(range(99))' '12 in d'
10000000 loops, best of 3: 0.0983 usec per loop
$ python -mtimeit -s'd=dict.fromkeys(range(99))' 'd.has_key(12)'
1000000 loops, best of 3: 0.21 usec per loop

尽管以下观察并非总是正确的,但您会注意到,通常在Python中,更快的解决方案更加优雅和Pythonic。这就是为什么如此-mtimeit有用的原因- 不仅仅是在这里和那里节省一百纳秒!-)


4
为此,使得验证“ in some_dict”实际上是O(1)变得容易得多(尝试将99增大为1999,您会发现运行时间大致相同)。
亚当·帕金

2
has_key似乎也是O(1)。
dan-gph 2015年


42

使用dict.has_key()如果(且仅当)你的代码是要求Python版本早于2.3(当为可运key in dict介绍)。


1
2013年的WebSphere更新使用Jython 2.1作为主要脚本语言。因此,不幸的是,在您注意到它五年之后,仍然需要注意这一点。
ArtOfWarfare 2014年

23

有一个例子in实际上会削弱您的表现。

如果你使用in一个O(1)集装箱只实现__getitem__has_key()而不是__contains__你会变成一个O(1)搜索到O(N),搜索(如in回落到通过线性搜索__getitem__)。

修复显然是微不足道的:

def __contains__(self, x):
    return self.has_key(x)

6
此答案在发布时适用,但99.95%的读者可以放心地忽略它。在大多数情况下,如果您使用的是这种晦涩难懂的方法,您就会知道。
wizzwizz4 '18

2
这确实不是问题。has_key()针对Python 2字典in/ __contains__是正确使用的API;对于那些不可避免要进行全面扫描的容器,无论如何都没有has_key()方法,如果有O(1)方法,那将是用例专用的,因此由开发人员来选择解决问题的正确数据类型。
马丁·彼得斯

15

has_key是一个字典方法,但是in可以在任何集合上使用,即使__contains__丢失,in也可以使用任何其他方法来迭代该集合以找出答案。


1
并且确实适用于迭代器“ x in xrange(
90,200

1
…:这似乎是一个非常糟糕的主意:用50次而不是2次进行操作。
Clément16年

1
@Clément在Python 3中,inrange对象进行测试实际上非常有效。不过,我不太确定它在Python 2上的效率xrange。;)
下午18年

@Clément不在Python 3中;__contains__可以简单地计算一个值是否在范围内。
马丁·彼得

1
@AlexandreHuat您的计时包括range每次创建新实例的开销。使用单个预先存在的实例,在我看来,“范围内的整数”测试快约40%。
MisterMiyagi

14

dict.has_key()的解决方案已弃用,请使用“ in”-sublime文本编辑器3

在这里,我举了一个名为“ age”的字典的例子-

ages = {}

# Add a couple of names to the dictionary
ages['Sue'] = 23

ages['Peter'] = 19

ages['Andrew'] = 78

ages['Karren'] = 45

# use of 'in' in if condition instead of function_name.has_key(key-name).
if 'Sue' in ages:

    print "Sue is in the dictionary. She is", ages['Sue'], "years old"

else:

    print "Sue is not in the dictionary"

6
正确,但是已经回答了,欢迎使用Stackoveflow,感谢您的示例,但是请务必检查答案!
igorgue '16

@igorgue我不确定对她的否决。她的答案可能与已经回答的答案相似,但她提供了一个示例。这是否足以回答SO?
阿克沙特·阿加瓦尔

14

亚当·帕金(Adam Parkin)的评论扩展了Alex Martelli的性能测试...

$ python3.5 -mtimeit -s'd=dict.fromkeys(range( 99))' 'd.has_key(12)'
Traceback (most recent call last):
  File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 301, in main
    x = t.timeit(number)
  File "/usr/local/Cellar/python3/3.5.2_3/Frameworks/Python.framework/Versions/3.5/lib/python3.5/timeit.py", line 178, in timeit
    timing = self.inner(it, self.timer)
  File "<timeit-src>", line 6, in inner
    d.has_key(12)
AttributeError: 'dict' object has no attribute 'has_key'

$ python2.7 -mtimeit -s'd=dict.fromkeys(range(  99))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0872 usec per loop

$ python2.7 -mtimeit -s'd=dict.fromkeys(range(1999))' 'd.has_key(12)'
10000000 loops, best of 3: 0.0858 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(  99))' '12 in d'
10000000 loops, best of 3: 0.031 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d'
10000000 loops, best of 3: 0.033 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(  99))' '12 in d.keys()'
10000000 loops, best of 3: 0.115 usec per loop

$ python3.5 -mtimeit -s'd=dict.fromkeys(range(1999))' '12 in d.keys()'
10000000 loops, best of 3: 0.117 usec per loop

精彩的统计信息,有时隐含的总比显式的好(至少在效率上)...
varun

谢谢@varun。我已经忘记了这个答案。我需要更频繁地进行这种测试。我经常阅读长篇文章,人们在争论着“最佳方式”来做事。但是我很少记得获得证明有多容易。
布鲁诺·布罗诺斯基

0

如果您有这样的事情:

t.has_key(ew)

将其更改为以下版本以在Python 3.X及更高版本上运行:

key = ew
if key not in t

6
不,你把考试倒了。如果值引用也是字典中的键,则t.has_key(ew)返回。如果值不在字典中,则返回。而且,别名非常非常冗余。正确的拼写是。这是8年前公认的答案已经告诉您的。Trueewkey not in tTruekey = ewif ew in t
马丁·彼得斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.