ChatGPT解决这个技术问题 Extra ChatGPT

我应该在 Python dicts 上使用“has_key()”还是“in”?

鉴于:

>>> d = {'a': 1, 'b': 2}

以下哪项是检查 'a' 是否在 d 中的最佳方法?

>>> 'a' in d
True
>>> d.has_key('a')
True

b
bluish

in 肯定更 Pythonic。

事实上has_key() was removed in Python 3.x


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

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 中,更快的解决方案更优雅且更符合 Python 风格;这就是 -mtimeit 如此有用的原因 - 只是在这里和那里节省一百纳秒!-)


感谢这一点,使验证“in some_dict”实际上更容易 O(1)(尝试将 99 增加到 1999 年,您会发现运行时大致相同)。
has_key 似乎也是 O(1)。
j
jamylak

根据 python docs

has_key() 被弃用,取而代之的是 d 中的 key。


has_key() 现在已在 Python 3 中移除
M
Mike Samuel

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


2013 年的 WebSphere 更新使用 Jython 2.1 作为其主要脚本语言。因此,不幸的是,在您注意到它五年后,这仍然是一件有用的事情。
s
schlenk

有一个示例 in 实际上会影响您的性能。

如果您在仅实现 __getitem__has_key() 但未实现 __contains__ 的 O(1) 容器上使用 in,您会将 O(1) 搜索变为 O(N) 搜索(因为 in 落入通过 __getitem__ 回到线性搜索)。

修复显然是微不足道的:

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

这个答案在发布时是适用的,但 99.95% 的读者可以放心地忽略它。在大多数情况下,如果您正在使用这种晦涩难懂的东西,您就会知道。
这真的不是问题。 has_key() 特定于 Python 2 字典in / __contains__ 是要使用的正确 API;对于那些无法避免全面扫描的容器,没有 has_key() 方法无论如何,如果有 O(1) 方法,那么这将是特定于用例的,因此取决于开发人员为问题选择正确的数据类型。
G
Greena modi

不推荐使用 dict.has_key() 的解决方案,使用 'in' -- sublime text editor 3

在这里,我举了一个名为'ages'的字典示例 -

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"

正确,但已经回答了,欢迎来到 Stackoveflow,感谢您的示例,但请务必检查答案!
@igorgue 我不确定对她的反对意见。她的回答可能与已经回答的类似,但她提供了一个例子。这还不足以成为SO的答案吗?
B
Bruno Bronosky

用 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。我忘记了这个答案。我需要更频繁地进行这种测试。我经常阅读长篇文章,人们在其中争论 The Best Way™ 做事。但我很少记得获得证据是多么容易。
该实验有一个缺陷,它将字典创建时间与密钥搜索时间混合在一起。最好将两者分开来衡量仅在关键搜索上花费的时间。一旦你将两者分开,计时结果将显示'key in D'和'key in D.keys()'似乎都是O(1)。没有本质区别,虽然 D.keys() 中的 key 比 D 中的 key 慢一点,但不是 O(N) vs O(1)。
我使用了python3,所以我得出的结论是python3(在python2中可能是O(N)vs O(1)),但我在python3中没有看到这一点。
u
u0b34a0f6ae

has_key 是一种字典方法,但 in 将适用于任何集合,即使缺少 __contains__in 也会使用任何其他方法迭代集合以找出答案。


并且也适用于迭代器“x in xrange(90, 200) <=> 90 <= x < 200”
…:这看起来是个非常糟糕的主意:50 次操作而不是 2 次。
@Clément 在 Python 3 中,对 range 对象进行 in 测试实际上非常有效。不过,我不太确定它在 Python 2 xrange 上的效率。 ;)
@Clément 不在 Python 3 中; __contains__ 可以简单地计算 值是否在范围内。
@AlexandreHuat 您的时间包括每次创建新 range 实例的开销。使用单个 预先存在的 实例,“范围内的整数”测试在我的计时中快了大约 40%。
K
Kirby

如果你有这样的事情:

if d.has_key('a'):

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

if 'a' in d:

不,你颠倒了测试。如果值 ew 引用也是字典中的键,则 t.has_key(ew) 返回 True。如果字典中的值是 not,则 key not in t 返回 True。此外,key = ew 别名非常非常多余。正确的拼写是 if ew in t。这是 8 年前公认的答案已经告诉你的。
改变了答案。 =) 只是为了避免人们误导。