Initialize Python variable from possible missing dict value
What is the Pythonic way to do this?
value = (mydict.has_key('index') and mydict['index']) or 1024
Initialize from a dict's value, if present, otherwise use default value.
Got some answers on LBYL and EAFP, but too verbose code :p
I开发者_如何转开发 want some one-line-initialize.
PS: python-2.4 only (running CentOS5)
value = mydict.get('index', 1024)
(Note that this is not exactly equivalent to your code, since it will use 1024
only if the key index
is not present, while your code also uses 1024
when the value corresponding to key index
is falsy. From your explanation I infer the former is what you actually want.)
Alternatively use setdefault()
- like get()
, but it inserts the key before returning the value:
value = mydict.setdefault('index', 1024)
The get
and setdefault
methods are important to know, they should be in any Python programmer's toolbox.
Another important way of doing this is to use collections.defaultdict
, because then you can define your "default" in a single place without having to scatter it throughout your code wherever the dictionary is used, i.e. Don't Repeat Yourself. Then if you change the default you don't need to hunt through your code finding all those gets and setdefaults. For example
>>> import collections
>>> myDict = collections.defaultdict
>>> myDict = collections.defaultdict(lambda : 1024)
>>> myDict[0] += 1
>>> print myDict[0], myDict[100]
1025 1024
Because you get to set the function call, you can do whatever you like for your default value. How about assigning a random value between 1 and 10 for every new key?
>>> import collections
>>> import random
>>> myDict = collections.defaultdict(lambda : random.randint(1,10))
>>> print myDict[0], myDict[100]
1 5
>>> print myDict[0], myDict[100], myDict[2]
1 5 6
OK, that example's a little contrived, but I'm sure you can think of better uses. Say you have an expensive class to construct - the instance is only created if the key is not actually present, unlike when calling get
and setdefault
, e.g.
>>> class ExpensiveClass(object):
>>> numObjects = 0
>>> def __init__(self):
>>> ExpensiveClass.numObjects += 1
>>>
>>> print ExpensiveClass.numObjects
0
>>> x = {}
>>> x[0] = ExpensiveClass()
>>> print ExpensiveClass.numObjects
1
>>> print x.get(0, ExpensiveClass())
<__main__.ExpensiveClass object at 0x025665B0>
>>> print ExpensiveClass.numObjects
2
>>> ExpensiveClass.numObjects = 0
>>> z = collections.defaultdict(ExpensiveClass)
>>> print ExpensiveClass.numObjects
0
>>> print z[0]
>>> <__main__.ExpensiveClass object at 0x02566510>
>>> print ExpensiveClass.numObjects
1
Note that the call to x.get
constructs a new ExpensiveClass
instance, even though we don't ever use that instance because x[0]
already exists. This could cause problems if you were indeed trying to count the number of ExpensiveClass
objects, or if they were very slow to create. These problems don't occur for collections.defaultdict
.
The collections
module defaultdict
class might be of help. Unfortunately it wasn't introduced until Python 2.5 and it doesn't do exactly what your code does. Here's a few differences:
- It only supplies a value when a key is missing, not when it is there but has a value considered
False
. - If the looked-up key is missing it will add it to the dictionary with a default value as well as return the default value.
- All missing values get the same default value.
If that's OK you can emulate the essence of one in older Pythons with something like this:
try:
from collections import defaultdict
except ImportError:
class defaultdict(dict):
def __init__(self, default_factory=None, *a, **kw):
dict.__init__(self, *a, **kw)
self.default_factory = default_factory
def __getitem__(self, key):
try:
return dict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)
def __missing__(self, key):
self[key] = value = self.default_factory()
return value
A recipe for a more complete emulation is available on ActiveState. Even if you had Python 2.5+, you might want to derive your own class to customize to have the exact behavior you wanted. Besides checking for "False" truth values and perhaps not adding any missing ones to the dictionary, you could also provide it with a separate dictionary of multiple default values corresponding to different keys instead of "one size fits all". For ways to do that see Is there a way to set multiple defaults on a Python dict using another dict?.
精彩评论