Benchmarking old-style and new-style Python classes
It has been many years since Python developers were really supposed to worry about new-style vs. old-style classes. There is only one style (new) in Python 3.x, and even in Python 2.x, old-style classes have not been recommended for many years. Nevertheless, I mention old-style classes in my Python courses, mostly so that participants will understand the potentially serious implications of creating classes without inheriting from object. For example:
>>> class Foo(object): pass >>> type(Foo) type >>> f = Foo() >>> type(f) __main__.Foo
The above is the way that modern Python programmers define classes. This is the preferred way, for sure; if you’re writing old-style classes, then you’re almost certainly doing something wrong. But it’s so easy to create an old-style class in Python — all you have to do is forget to inherit from “object”:
>>> class Foo(): pass >>> type(Foo) classobj >>> f = Foo() >>> type(f) instance
As you can see, the fact that I created an old-style class directly affects the types of objects that I have created, and thus their capabilities. For many years, it has been seen as a mistake to create old-style classes; not only are you missing out on new functionality, but you are creating objects that behave differently from the rest of objects in Python.
I was just teaching a Python class at a company that has a fair amount of legacy Python code. It turns out that this legacy code includes a large number of old-style classes. The company asked me whether it was worth upgrading all of their old-style classes to use new-style classes; my answer was that (1) if it ain’t broke, don’t fix it, (2) it’s hard to know whether the upgrade would be trivially easy or impossibly hard, and (3) you’ll likely want to upgrade these classes over time, doing so incrementally.
Someone then asked me whether there is a performance difference between old-style and new-style classes, in order to evaluate the importance of doing such an upgrade project. I had to admit that I wasn’t sure, and couldn’t find anything online (after doing a quick search) on the subject. I thus decided to do a small benchmark to see what might be faster (or slower). I’m not an expert in benchmarking, but I did want to check the basic speed of (1) object creation, (2) inheritance, and (3) implementation of __repr__.
The results surprised me: New-style classes are substantially faster. Here is the benchmark that I ran on the new-style class:
class Person(object): def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name def fullname(self): return self.first_name + " " + self.last_name def __repr__(self): return self.fullname() class Employee(Person): def __init__(self, first_name, last_name, employee_id): Person.__init__(self, first_name, last_name) self.employee_id = employee_id def test_employee(): e = Employee('first', 'last', 1) return str(e)
My test of old-style classes was precisely the same, except that I omitted “object” between the parentheses in the class definition of Person.
I used %timeit from within IPython to run the function 100,000 times for each of the two versions (old-style and new-style). The results surprised me: Old-style classes took 3.09 µs per iteration, while new-style classes took 2.44 µs per iteration, a difference of more than 20 percent!
The bottom line would seem to be that if you’re running large systems in Python and are still using old-style classes, it’s not just worth upgrading to new-style classes for reasons of aesthetics, features, and compatibility. It’s also going to speed up your code, particularly if you have a large, long-running system that invokes lots of methods.