On this year’s PyCon US, there was a talk with rather (thought-)provoking title Stop Writing Classes. The speaker might not be the most charismatic one you’ve listened to, but his point is important, even if very simple. Whenever you have class with a constructor and just one other method, you could probably do better by turning it into a single function instead.
Examples given in the presentation were in Python, of course, but the whole advice is pretty generic. It can be applied with equal success even to languages that are object-oriented to the extreme (like Java): just replace ‘function’ with ‘static method’. However, if we are talking about Python, there are many more situations where we can replace classes with functions. Often this will result in simpler code with less nesting levels.
Let’s see a few examples.
Sometimes we want to construct many similar objects that differ only slightly in a way their constructors are invoked. A rather simple example would be a
urllib2.Request with some custom HTTP headers included:
That works, but it’s unnecessarily complex without adding any notable benefits. It’s unlikely that we ever want to perform an
isinstance check to distinguish between
CustomRequest and the original
Request, which is the main “perk” of using class-based approach.
Indeed, we could do just as well with a function:
Note how usage doesn’t even change, thanks to Python handling classes like any other callables. Also, notice the reduced amount of underscores ;)
Even if the method we want to override is not
__init__, it might still make sense to not do it through inheritance. Python allows to add or replace methods of specific objects simply by assigning them to some attribute. This is commonly referred to as monkey patching and it enables to more or less transparently change behavior of most objects once they have been created:
You will likely say that this look more hackish than using inheritance and/or decorators, and you’ll be correct. In some cases, though, this might be a right thing. If the solution for the moment is indeed a bit hacky, “disguising” it into seemingly more mature and idiomatic form is unwarranted pretension. Sometimes a hack is fine as long as you are honest about it.
Coming to Python from a more strict language, like C++ or Java, you may be tempted to construct types such as this:
An idea is to encapsulate some common piece of data and pass it along in uniform way. In compiled, statically typed languages this is a good way to make the type checker work for us to eliminate certain kind of bugs and errors. If we declare a function to take
ContentType, we can be sure we won’t get anything else. As a result, once we convert the initial string (like
"application/json") into an object somewhere at the edge of the system, the rest of it can be simpler: it doesn’t have to bother with strings anymore.
But in dynamically typed, interpreted languages you can’t really extract such benefits because there is no compiler you can instruct to do your bookkeeping. Although you are perfectly allowed to write analogous classes:
there is no real benefit in doing so. Since you cannot be bulletproof-sure that a function will only receive objects of your type, a better solution (some would say “more pythonic”) is to keep the data in original form, or a simple form that is immediately usable. In this particular case a raw string will probably do best, although a tuple
("text", "html") – or better yet,
namedtuple – may be more convenient in some applications.
…stop writing classes. Not literally all of them, of course, but always be on the lookout for alternatives. More often than not, they tend to make code (and life) simpler and easier.
Classes are indeed overrated :) After using Clojure for some time, I’m writing my current Python project with functions only – no classes yet!
“Don’t”s are considered harmful.
classes in php are also bull#@ ;)