Classes in Python: Insights from PyCon 2012
How do classes and their instances work in Python?
In Python, classes are basically a bunch of namespace tricks.
- Namespaces are “a mapping from names to objects” (like dictionaries).
- So when you say
instance.attribute, Python’s going to look in the instance dictionary, then the class dictionary, then the class’s parent class’s dictionary… (How: 1, 2. Multiple inheritance is a little trickier.)
- To put a name in the instance dictionary, you need to tell Python that using
self.attribute_namein one of the functions defined in the class definition (typically in
super) refers to that instance (of the class's ancestry tree).
Why use classes?
Classes are useful “when you have … mutable data, and … related functions that you want to use with that data”. There are often better tools:
- For example, in the heapq module, where all the functions include some contemplated mutable data as a parameter, using a class makes sense.
- On the other hand, if you find yourself using the same function with the same argument over and over, use
functools.partialto create another function with those arguments fixed.
So classes are the optimal solution less often than you might think. Given this:
- Refactor religiously to reduce the number of methods in a class.
- Then, if the class has two methods left, one of which is
__init__, turn the other method into a standalone function and discard everything else.
Though classes can also be useful for creating small namespaces, subclassing is for code re-use (“don’t repeat yourself”), not hierarchy creation. In other words, sometimes it’s natural to have Animal subclass Dog, not vice versa.
The standard library has a very flat namespace. — Jack Diederich
How to maximise code re-use using classes
Fundamentals: A subclass can do one or more of…
- Adding methods beyond those of its superclass.
- Overriding the methods of its superclass.
- Keeping, but extending, the methods of its superclass. (Unsure how.)
- Liskov Substitution Principle: If S is a subtype of T, then objects of type T may be replaced with objects of the S (using same method names, etc).
- Isolate/minimise impact of LSP violations, e.g. factor them out such that a subclass can override (ideally) just one method (e.g. Guido’s MutableSet — the constructor is called indirectly via the method _from_iterable()).
- If certain functions (methods) rely on other functions in that class staying the same, you can keep them overridable (by subclasses) via a namespace trick; see the official tutorial or language reference for more.
The ‘framework’ design pattern:
- Keep control logic in the parent class, override in the children.
- Don’t need to write all the control logic in advance: use dynamic dispatch, i.e. the class has a function which sends that work elsewhere (e.g., a method in the subclass). (Don’t yet fully understand why this is useful.)
Take advantage of magic methods:
For example, a useful way to define a class’s
return self.__class__.__name__. That way instances of subclasses get the name of that subclass.
- Become a better Python programmer: read standard library code.
- Standard library exceptions are more readable for others; you rarely need to write your own.
- cmd.py makes writing a command line interface for Python programs easy.