dict.pop or dict.get and evaluation
Asked Answered
B

2

5

Consider the following code:

>>> def default_answer():
...     print "Default was required!"
...     return 100
... 
>>> g = { 'name': 'Jordan', 'age': 35 }
>>> result = g.get('age', default_answer())
Default was required!
>>> result = g.pop('age', default_answer())
Default was required!

Notice that whether g contains the expected key or not, default_answer is called. This makes sense programmatically but if default_answer was computationally expensive this would be a hassle (to run a lot of code to create a value that was going to be thrown away).

The only way I can think of writing this without always calling default_answer is:

result = g.pop('age', False) or default_answer()

Is this the best way to do it?

(Note that I'm aware that replacing default_answer with an object with lazy evaluation would also solve part of this problem, but that's outside the scope of this question).

Birth answered 27/8, 2012 at 18:51 Comment(8)
Is try: ... except: off the table for some reason?Saldana
Why would you use pop for this instead of get?Domino
Why aren't you using a defaultdict (docs.python.org/library/…) ?Airlia
@Domino in this case I want the value gone from the dictionary after I've retrieved it.Birth
@Airlia assume that I didn't actually create g myself and am receiving it as an input...Birth
@mouad: A defaultdict always comes with the danger of hiding "real" KeyErrors.Freak
@JordanReiter -- Sorry, I misunderstood. I thought you were trying to decide between using get or pop. You're looking for an idiom that works with either.Domino
@Domino in fairness, Sven's answer below would not work with result = g.get('age') although it would work with result = g['age'].Birth
F
10

No, this is not the best way to do this, since the result of g.pop() might be "falsy". Use try/except instead:

try:
    result = g.pop('age')
except KeyError:
    result = default_answer()

This idiom is called EAFP and is usually preferred over LBYL for various reasons:

  1. It avoids looking up the key twice (which is not an issue with an actual dict, but might be suboptimal with other mappings).

  2. It avoids a race condition in threaded code.

  3. Many people find it easier to read.

Freak answered 27/8, 2012 at 18:53 Comment(1)
I was going for a one liner but I've always been a big fan of EAFP in Python so I'll probably try this instead.Birth
L
2

That is an okay way as long as you know that any boolean-false value (not just False but also empty string, empty list, etc.) should be replaced by your default. You can always do it explicitly:

if 'age' in g:
    result = g['age']
else:
    result = default_answer()

(Or an equivalent version with try/except where you read the value first and do the default in an except clause. Which one is better depends on whether you expect the missing-value case to be common or uncommon.)

Liquor answered 27/8, 2012 at 18:53 Comment(1)
In Python the try except is the preferred way. However, one might consider the if else here, as it allows to use an oneliner: result = g['age'] if 'age' in g else default_answer().Weigela

© 2022 - 2024 — McMap. All rights reserved.