Yes, yes, I know that Python dictionaries are instances of the dict class. Something that I have noticed through the years and in a variety of bits of code is that many people will use a dictionary where they should really be using an instance. Many of the people that do this likely came from PHP4, Perl or even C where classes are either non-existent or are a little uncomfortable to use.
Dictionaries are great if you need to pass around a few values in one limited place in a larger system. Custom class instances are much better, though, if you have some data that is going to be used in many places in your code.
This is not a rant about “you should be doing OO design”. As we’ll see in a moment, I’m not even writing about OO design. I’m advocating using custom class instances instead of plain dictionaries because Python is a class-based object oriented language. That means that the language specifically provides benefits to people using classes.
First, I should make it clear what exactly I’m talking about. I’ll use the simple example of a person. A person could be represented with a dictionary:
Some people would probably jump out and say that the second example is a lot more verbose and doesn’t let you store whatever data values you want as you can in the first example. The thing is that you only define the Person class once. The instantiation of a Person is just as easy as the instantiation of a dict. And, if you want to make the class smaller and support an unlimited number of attributes, you could do this if you really want:
If you are creating a given data structure in many different spots in your program, this can be a problem if you’re using a dictionary and need to add a new key/value pair. With a class, you can just do this:
And now, everywhere a Person is used you know you’re not going to get an AttributeError for person.age. If you use dictionaries everywhere, you’ll get a KeyError if you try to access ‘age’ on a dictionary created by older code.
3. Deprecations.
Even better than just providing backwards compatibility is to issue a deprecation warning so that you can clean up uses of the old style.
For many types of behaviors, though, I think it’s handy to have the behavior on the object you’re trying to act on. This potentially saves you from extra imports. Consider this case:
Custom class instances have the advantages I note above, just using them as basic data containers. Classes also offer inheritance and metaclasses which allow you to share attributes and behavior in a way that a plain dictionary can’t. Classes also give you operator overloading, customizable attribute access and the ability to customize the str and repr forms of the objects.
There’s really very little downside to using custom classes and quite a few benefits, particularly if you’ve got a system that’s going to grow. If you tend to reach for dictionaries whenever you need to store some data, you might consider whipping up a quick class instead.