开发者

Determine whether a key is present in a dictionary [duplicate]

开发者 https://www.devze.com 2023-01-16 07:45 出处:网络
This question already has answers here: Check if a given key already exists in a dictionary 开发者_运维知识库(16 answers)
This question already has answers here: Check if a given key already exists in a dictionary 开发者_运维知识库 (16 answers) Closed 16 hours ago.

I have a Python dictionary like mydict = {'name':'abc','city':'xyz','country','def'}.

How should I check if a key is in the dictionary or not? I know these ways already:

if mydict.has_key('name'):

if 'name' in mydict:


if 'name' in mydict:

is the preferred, pythonic version. Use of has_key() is discouraged, and this method has been removed in Python 3.


In the same vein as martineau's response, the best solution is often not to check. For example, the code

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

is normally written

foo = d.get(x, bar)

which is shorter and more directly speaks to what you mean.

Another common case is something like

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

d[x].append(foo)

which can be rewritten

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

or rewritten even better by using a collections.defaultdict(list) for d and writing

d[x].append(foo)


In terms of bytecode, in saves a LOAD_ATTR and replaces a CALL_FUNCTION with a 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             

My feelings are that in is much more readable and is to be preferred in every case that I can think of.

In terms of performance, the timing reflects the opcode

$ 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 is almost twice as fast.


My answer is "neither one".

I believe the most "Pythonic" way to do things is to NOT check beforehand if the key is in a dictionary and instead just write code that assumes it's there and catch any KeyErrors that get raised because it wasn't.

This is usually done with enclosing the code in a try...except clause and is a well-known idiom usually expressed as "It's easier to ask forgiveness than permission" or with the acronym EAFP, which basically means it is better to try something and catch the errors instead for making sure everything's OK before doing anything. Why validate what doesn't need to be validated when you can handle exceptions gracefully instead of trying to avoid them? Because it's often more readable and the code tends to be faster if the probability is low that the key won't be there (or whatever preconditions there may be).

Of course, this isn't appropriate in all situations and not everyone agrees with the philosophy, so you'll need to decide for yourself on a case-by-case basis. Not surprisingly the opposite of this is called LBYL for "Look Before You Leap".

As a trivial example consider:

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

Although in the case it's almost exactly the same amount of code, the second doesn't spend time checking first and is probably slightly faster because of it (try...except block isn't totally free though, so it probably doesn't make that much difference here).

Generally speaking, testing in advance can often be much more involved and the savings gain from not doing it can be significant. That said, if 'name' in dict: is better for the reasons stated in the other answers.

If you're interested in the topic, this message titled "EAFP vs LBYL (was Re: A little disappointed so far)" from the Python mailing list archive probably explains the difference between the two approached better than I have here. There's also a good discussion about the two approaches in the book Python in a Nutshell, 2nd Ed by Alex Martelli in chapter 6 on Exceptions titled Error-Checking Strategies. (I see there's now a newer 3rd edition, publish in 2017, which covers both Python 2.7 and 3.x).

0

精彩评论

暂无评论...
验证码 换一张
取 消