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

igo*_*gue 867 python dictionary

我想知道做什么更好:

d = {'a': 1, 'b': 2}
'a' in d
True
Run Code Online (Sandbox Code Playgroud)

要么:

d = {'a': 1, 'b': 2}
d.has_key('a')
True
Run Code Online (Sandbox Code Playgroud)

ton*_*nfa 1223

in 肯定是更加pythonic.

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

  • 要避免的一个半问题是确保你这样做:"键入some_dict"而不是"键入some_dict.keys()".两者在语义上是等价的,但在性能方面,后者要慢得多(O(n)vs O(1)).我见过人们做"在dict.keys()"中认为它更明确,因此更好. (204认同)
  • @AdamParkin在Python 3中,`keys()`只是一个类似于字典而不是副本的视图,所以d.keys()中的`x是O(1).但是,`d in d`更像是Pythonic. (6认同)
  • 另外,在Python 3中,要检查值是否存在而不是键,请尝试在d.values()中>> >> 1 (3认同)
  • @AdamParkin我在答案http://stackoverflow.com/a/41390975/117471中展示了您的评论 (2认同)
  • @AdamParkin有意思,我没有看到.我想这是因为d.keys()中的`x必须构造和销毁一个临时对象,完成所需的内存分配,其中`x in d.keys()`只是进行算术运算(计算哈希值)并进行查找.请注意,`d.keys()`只是它的10倍左右,实际上仍然不长.我没有检查,但我仍然很确定它只是O(1). (2认同)
  • @WorkofArt 不可能,因为“None”是有效的字典值。 (2认同)

Ale*_*lli 250

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
Run Code Online (Sandbox Code Playgroud)

虽然以下观察并非总是如此,但您会注意到,通常,在Python中,更快的解决方案是更优雅和Pythonic; 这就是为什么-mtimeitSO有用 - 它不只是在这里和那里节省一百纳秒! - )

  • 多亏了这一点,确认"在some_dict中"实际上是O(1)更容易(尝试将99增加到1999年,你会发现运行时大致相同). (4认同)
  • `has_key`似乎也是O(1). (2认同)

Nad*_*mli 92

根据python 文档:

has_key()不赞成使用 key in d.

  • `has_key()` 现已在 Python 3 中删除 (4认同)

Joh*_*hin 39

使用dict.has_key()if(且仅当)您的代码需要由早于2.3的Python版本(key in dict引入时)运行.

  • 2013 年的 WebSphere 更新使用 Jython 2.1 作为其主要脚本语言。所以不幸的是,在你注意到它五年后,这仍然是一件有用的事情。 (3认同)

sch*_*enk 22

有一个例子in实际上会杀死你的表现.

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

修复显然是微不足道的:

def __contains__(self, x):
    return self.has_key(x)
Run Code Online (Sandbox Code Playgroud)

  • 这个答案在发布时适用,但99.95%的读者可以放心地忽略它.在最近的情况下,如果你正在处理这个模糊不清的东西,你就会知道它. (5认同)
  • 这真的不是问题。`has_key()` 是*特定于 Python 2 词典的*。`in` / `__contains__` 是要使用的正确 API;对于那些无法避免完全扫描的容器,无论如何*都没有`has_key()`方法,如果有 O(1) 方法,那么这将是特定于用例的,因此由开发人员选择问题的正确数据类型。 (2认同)

u0b*_*6ae 15

has_key是一个字典方法,但in可以在任何集合上工作,即使__contains__缺少,in也会使用任何其他方法迭代集合来查找.


小智 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"
Run Code Online (Sandbox Code Playgroud)

  • 正确,但它已经回答,欢迎来到Stackoveflow,谢谢你的例子,总是检查答案! (6认同)
  • @AkshatAgarwal否:这个问题已经有一个例子。 (3认同)

Bru*_*sky 13

亚当·帕金的评论扩展了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
Run Code Online (Sandbox Code Playgroud)