确定字典中是否存在密钥

Ava*_*esh 41 python dictionary

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

我有一个Python字典,如:

mydict = {'name':'abc','city':'xyz','country','def'}
Run Code Online (Sandbox Code Playgroud)

我想检查密钥是否在字典中.我很想知道从以下两个案例中哪个更为可取,为什么?

1> if mydict.has_key('name'):
2> if 'name' in mydict:
Run Code Online (Sandbox Code Playgroud)

Tim*_*ker 66

if 'name' in mydict:
Run Code Online (Sandbox Code Playgroud)

是首选的pythonic版本.has_key()不鼓励使用,并且已在Python 3中删除了此方法.

  • @PulpFiction:`dict.get(key)`当你(1)不是'KeyError`以防'key`不在dict(2)中时想要使用默认值(如果有)没有`key`(`dict.get(key,default)`).点#2也可以使用`defaultdict`来完成. (8认同)
  • 此外,dict`中的"name"将适用于任何可迭代而不仅仅是字典. (2认同)
  • 怎么样'dict.get(key)`?那还应该避免吗? (2认同)
  • dict.get返回值.它不会(也不能)可靠地告诉您密钥是否在字典中.这是一个完全不同的目的. (2认同)

Mik*_*ham 30

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

if x in d:
    foo = d[x]
else:
    foo = bar
Run Code Online (Sandbox Code Playgroud)

通常是写的

foo = d.get(x, bar)
Run Code Online (Sandbox Code Playgroud)

这更短,更直接地说明你的意思.

另一个常见的情况是

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

d[x].append(foo)
Run Code Online (Sandbox Code Playgroud)

这可以改写

d.setdefault(x, []).append(foo)
Run Code Online (Sandbox Code Playgroud)

或者通过使用collections.defaultdict(list)for d和write来改写甚至更好

d[x].append(foo)
Run Code Online (Sandbox Code Playgroud)

  • Naw,这些是Python随着时间的推移而演变的方法和类型.`)` (4认同)

aar*_*ing 13

在字节码方面,in保存LOAD_ATTR并替换CALL_FUNCTIONa 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             
Run Code Online (Sandbox Code Playgroud)

我的感觉是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
Run Code Online (Sandbox Code Playgroud)

in 差不多快了两倍.

  • @Mike Graham,你大部分都是对的.我确实在那里坚持更糟糕的情况,因为,IMO,那是你真正想知道的地方.另外,我认为你的态度(虽然仍然是绝对正确的),稍微更适合像C这样的语言,除非你真的搞砸了,否则它会很快.在Python中,它可以在更大程度上实现它.此外,核心开发人员还有一种方法可以调整"一种正确的方式"来做某事,这样,在一种语言中,性能是一种比正常更好程度的良好风格的良好指标. (2认同)

mar*_*eau 10

我的回答是"都不是".

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

这通常是通过将代码包含在一个try...except子句中来完成的,并且是一个众所周知的习惯用法,通常表达为" 请求宽恕而不是权限 "或缩写为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
Run Code Online (Sandbox Code Playgroud)

VS

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

虽然在这种情况下它几乎完全相同的代码量,但第二个不花时间检查第一个并且可能稍微快一点因为它(尝试...除了块不是完全免费的,所以它可能不在这里做出很大的改变).

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

如果你感兴趣的话题,这个消息 "标题(再次:有点遗憾至今)EAFP VS LBYL "从Python邮件列表归档或许可以解释两者的区别走近比我这里有.在关于标题为" 错误检查策略 "的异常章节中,Alex Martelli的第二版Ed-Nutshell中的两种方法也有很好的讨论.

  • Python中的例外代价很高.如果您希望密钥丢失的时间超过百分之几,则异常开销可能会占用该函数的运行时. (4认同)
  • @duffymo,Python中流行的风格是使用异常.这创建了更多惯用的可读代码.一般来说,一个成功的`try`块非常便宜,但是如果引发异常则会更加昂贵,但是*这并不代表你编写的95%代码的设计*. (2认同)
  • @Tim:你有没有想念我所说的"如果你希望钥匙丢失的时间超过百分之几"?如果它们没有发生,异常只会和if语句一样快 - 如果它们确实发生了,你的链接显示它们比零分割慢2倍,而我的快速时间显示它们对于dict查找慢了_10x_.螺旋"Pythonic",我会采用速度提高10倍的习语. (2认同)