I spent 2 hours tracking a bug yesterday caused by a bad habit I’ve realized I must break.
In Python I tend to create base classes to derive common functionality, a good practice. I also tend to set default attributes on the class, like so.
class Plant(object): species = 'not known' class Kale(Plant): species = 'brassica' def __init__(self, name): self.name = name
My intention is to create lots of Kale objects, some of them will be different varieties of Kale. The intention of the base class and even of the Kale class is to define some common methods and functionality more than it is to defined the data semantics which will be pre instance.
At this point I realize something, there can be multiple species of different plants, so I change my code.
class Plant(object): species = [] class Kale(Plant): species = ['brassica'] def __init__(self, name): self.name = name
This looks kosher, but it’s not. The problem is that when I first modeled the data I was just initializing species so that it would always be set to something, the information was expected to change per instance. I was using the class to derive unique functionality, not define the data as a class inheritance hierarchy. But continuing to lazily initialize attributes per class which are really suppose to be per instance will end up biting me in the ass.
spear_kale = Kale("Spear Kale") kai_lan = Kale("Kai-Lan") # A chinese form of Brassica oleracea kai_lan.species.append("Brassica oleracea")
You see the problem now. When species was a string new instances of Kale that changed it would change the reference to species for their instance to a new string. Now that it’s a list and we are using append we are appending instance information to the class attribute and spear_kale and any other new Kale instances will have the kai_lan species info attached to them. The initialization of the species list should have been done in __init__ because, once modified, it’s actually information that is per instance not per class.
If I would have been using a list in the beginning I probably never would have created it as a class attribute. Since Plant doesn’t have an __init__ and it seemed like a good idea to default new subclasses to _something_ and I continued to use that pattern in defining the defaults all the way up until it bit me.
This illustration is pretty straightforward, but today I was debugging an issue in a default added to a base class of a wsgi application class and it took me 2 hours to figure out why an attribute being accessed in one web server was actually pulling an attribute that should only be accessible from a different web server in the same Python. It was painful and as a result I’ll be much more diligent about defined defaults that are per instance in an __init__ upfront instead of being lazy.

