Python tricks: accessing dictionary items as object attributes

2014-06-17 by Senko Rašić

Python’s dictionaries are great for creating ad-hoc structures of arbitrary number of items. Sometimes, though, it can be awkward using the dictionary syntax for setting and getting the items. For example, let’s say you have the following data structures:

starfleet = [
 {
   'name': 'Jean-Luc Picard',
   'role': 'captain',
   'vessel': 'Enterprise',
   'episodes': 0
 }, ...
]
startrek = [
 {
   'title': 'Encounter at Fairpoint',
   'aired_on': date(1987, 9, 28),
   'characters': ['Jean-Luc Picard', 'Q', 'Deanna Troi']
 }, ...
]

And you have a following piece of code:

for person in starfleet:
    for episode in startrek:
        if person['name'] in episode['characters']:
            person['episodes'] = person['episodes'] + 1
    print "Person %s appears in %d episodes" % (person['name'],
       person['episodes'])

This works, but bracketing and quoting those field names is tedious and looks ugly. Something like this would be much nicer:

for person in starfleet:
    for episode in startrek:
        if person.name in episode.characters:
            person.episodes += 1
    print "Person %s appears in %d episodes %" % (person.name,
        person.episodes)

Writing the code to parse the dictionary and populate a Python object is not hard to do, but it’s boring boilerplate code that you have to write for each variant of the data structure, update when your data structure changes, and if you want to get the data back into dictionary (eg. for saving in JSON format), you need to write the boilerplate export code as well.

However, there’s a better way, taking advantage of Python’s dynamic features. Custom (ie. not built-in) objects in Python by default have a magic __dict__ attribute that holds all per-instance attributes of the object. But there’s no reason why we can’t supply our own dict instead! So if we have something like this:

class objectview(object):
    def __init__(self, d):
        self.__dict__ = d

then this works:

d = {'a': 1, 'b': 2}
o = objectview(d)
assert o.a == 1
assert o.b == 2
d['c'] = 3
assert o.c == 3
del o.c
assert 'c' not in d

The problem with this, however, is that this object doesn’t behave as a dictionary – if you want to use any of the dictionary methods, you need to access __dict__ directly (or use the dictionary that this object was created from).

Another approach is to subclass dict and add attribute getter and setter methods:

class objdict(dict):
    def __getattr__(self, name):
        if name in self:
            return self[name]
        else:
            raise AttributeError("No such attribute: " + name)

    def __setattr__(self, name, value):
        self[name] = value

    def __delattr__(self, name):
        if name in self:
            del self[name]
        else:
            raise AttributeError("No such attribute: " + name)

Which method is better? It depends. If you have existing dictionaries and you just want to access them as objects, the objectview approach is better. If you’re creating new dictionaries and want them to behave as both dictionaries and objects, the objdict method is nicer and less code, than adding a dozen wrapper methods (for items(), values(), keys(), __iter__(), __contains__(), etc.) to objectview.

Author
Senko Rašić
We’re small, experienced and passionate team of web developers, doing custom app development and web consulting.