开发者

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?.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜