确定字典中是否存在密钥

时间:2010-09-17 09:09:04

标签: python dictionary

  

可能重复:
  'has_key()' or 'in'?

我有一个Python字典,如:

mydict = {'name':'abc','city':'xyz','country','def'}

我想检查某个键是否在字典中。 我很想知道从以下两个案例中哪个更优选?为什么?

1> if mydict.has_key('name'):
2> if 'name' in mydict:

4 个答案:

答案 0 :(得分:59)

if 'name' in mydict:

是首选的pythonic版本。不鼓励使用has_key(),此方法has been removed in Python 3

答案 1 :(得分:27)

与martineau的回应一样,最好的解决方案通常是不检查。例如,代码

if x in d:
    foo = d[x]
else:
    foo = bar

通常是写的

foo = d.get(x, bar)

更短,更直接地说出你的意思。

另一种常见的情况是

if x not in d:
    d[x] = []

d[x].append(foo)

可以重写

d.setdefault(x, []).append(foo)

或使用collections.defaultdict(list) d并撰写

更好地重写
d[x].append(foo)

答案 2 :(得分:13)

就字节码而言,in会保存LOAD_ATTR并将CALL_FUNCTION替换为COMPARE_OP

>>> dis.dis(indict)
  2           0 LOAD_GLOBAL              0 (name)
              3 LOAD_GLOBAL              1 (d)
              6 COMPARE_OP               6 (in)
              9 POP_TOP             


>>> dis.dis(haskey)
  2           0 LOAD_GLOBAL              0 (d)
              3 LOAD_ATTR                1 (haskey)
              6 LOAD_GLOBAL              2 (name)
              9 CALL_FUNCTION            1
             12 POP_TOP             

我的感受是in 更具可读性,并且在我能想到的每种情况下都是首选。

在性能方面,时间反映了操作码

$ python -mtimeit -s'd = dict((i, i) for i in range(10000))' "'foo' in d"
 10000000 loops, best of 3: 0.11 usec per loop

$ python -mtimeit -s'd = dict((i, i) for i in range(10000))' "d.has_key('foo')"
  1000000 loops, best of 3: 0.205 usec per loop

in几乎快了两倍。

答案 3 :(得分:10)

我的回答是“都不是”。

我认为最“Pythonic”做事的方法是不事先检查密钥是否在字典中,而只是编写假定它在那里的代码并捕获任何因为它不被引发而引起的KeyErrors。

通常将代码括在try...except子句中,这是一个众所周知的习惯用法,通常表示为“更容易请求宽恕而非许可”或首字母缩略词< strong> EAFP ,这基本上意味着最好先尝试一下并抓住错误,以确保在做任何事情之前一切正常。当您可以优雅地处理异常而不是试图避免它们时,为什么验证不需要验证的内容?因为它通常更具可读性,并且如果概率很低,代码往往更快,那么密钥将不存在(或者可能存在任何先决条件)。

当然,这并不适用于所有情况,并不是每个人都同意这一理念,因此您需要根据具体情况自行决定。毫不奇怪,与此相反的是LBYL,称之为“Look Before You Leap”。

作为一个简单的例子考虑:

if 'name' in dct:
    value = dct['name'] * 3
else:
    logerror('"%s" not found in dictionary, using default' % name)
    value = 42

VS

try:
    value = dct['name'] * 3
except KeyError:
    logerror('"%s" not found in dictionary, using default' % name)
    value = 42

虽然在这种情况下几乎完全相同的代码量,但是第二次没有花费时间先检查,因为它可能会稍快一些(尝试...除了块之外并不完全免费,所以它可能这里没有太大的区别。)

一般来说,提前进行测试通常可以更多地参与进来,而不进行测试所带来的节省可能会很大。也就是说,if 'name' in dict:因其他答案中所述的原因而更好。

如果您对该主题感兴趣,那么这篇名为“ EAFP vs LBYL”(来自Re:有点失望)的message来自Python邮件列表存档可能会解释两者之间的差异接近比我在这里更好。 Alex Martelli在题为Python in a Nutshell, 2nd Ed的例外情况的第6章中对Error-Checking Strategies一书中的两种方法进行了很好的讨论。 (我看到现在有一​​个更新的3rd edition,2017年发布,其中包括Python 2.7和3.x)。