Julien Danjou: The definitive guide on how to use static, class or abstract methods in Python
>>> class Pizza(object):
... def __init__(self, size):
... self.size = size
... def get_size(self):
... return self.size
...
>>> Pizza.get_size
<unbound method Pizza.get_size>
What Python tells you here, is that the attribute get_size of the class Pizza is a method that is unbound. What does this mean? We'll know as soon as we'll try to call it:
>>> Pizza.get_size()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unbound method get_size() must be called with Pizza instance as first argument (got nothing instead)
We can't call it because it's not bound to any instance of Pizza. And a method wants an instance as its first argument (in Python 2 it must be an instance of that class; in Python 3 it could be anything). Let's try to do that then:
>>> Pizza.get_size(Pizza(42))
42
It worked! We called the method with an instance as its first argument, so everything's fine. But you will agree with me if I say this is not a very handy way to call methods; we have to refer to the class each time we want to call a method. And if we don't know what class is our object, this is not going to work for very long. So what Python does for us, is that it binds all the methods from the class Pizza to any instance of this class. This means that the attribute get_size of an instance of Pizza is a bound method: a method for which the first argument will be the instance itself.
>>> Pizza(42).get_size
<bound method Pizza.get_size of <__main__.Pizza object at 0x7f3138827910>>
>>> Pizza(42).get_size()
42
As expected, we don't have to provide any argument to get_size, since it's bound, its self argument is automatically set to our Pizza instance. Here's a even better proof of that:
>>> m = Pizza(42).get_size
>>> m()
42
Indeed, you don't even have to keep a reference to your Pizza object. Its method is bound to the object, so the method is sufficient to itself. But what if you wanted to know which object this bound method is bound to? Here's a little trick:
>>> m = Pizza(42).get_size
>>> m.__self__
<__main__.Pizza object at 0x7f3138827910>
>>> # You could guess, look at this:
...
>>> m == m.__self__.get_size
True
Obviously, we still have a reference to our object, and we can find it back if we want. In Python 3, the functions attached to a class are not considered as unbound method anymore, but as simple functions, that are bound to an object if required. So the principle stays the same, the model is just simplified.
>>> class Pizza(object):
... def __init__(self, size):
... self.size = size
... def get_size(self):
... return self.size
...
>>> Pizza.get_size
<function Pizza.get_size at 0x7f307f984dd0>
Static methods Static methods are a special case of methods. Sometimes, you'll write code that belongs to a class, but that doesn't use the object itself at all. For example:
class Pizza(object):
@staticmethod
def mix_ingredients(x, y):
return x + y
def cook(self):
return self.mix_ingredients(self.cheese, self.vegetables)
In such a case, writing mix_ingredients as a non-static method would work too, but it would provide it a self argument that would not be used. Here, the decorator @staticmethod buys us several things:
- Python doesn't have to instantiate a bound-method for each Pizza object we instiantiate. Bound methods are objects too, and creating them has a cost. Having a static method avoids that:
>>> Pizza().cook is Pizza().cook
False
>>> Pizza().mix_ingredients is Pizza.mix_ingredients
True
>>> Pizza().mix_ingredients is Pizza().mix_ingredients
True
- It eases the readability of the code: seeing @staticmethod, we know that the method does not depend on the state of object itself;
- It allows us to override the mix_ingredients method in a subclass. If we used a function mix_ingredients defined at the top-level of our module, a class inheriting from Pizza wouln't be able to change the way we mix ingredients for our pizza without overriding cook itself.
>>> class Pizza(object):
... radius = 42
... @classmethod
... def get_radius(cls):
... return cls.radius
...
>>>
>>> Pizza.get_radius
<bound method type.get_radius of <class '__main__.Pizza'>>
>>> Pizza().get_radius
<bound method type.get_radius of <class '__main__.Pizza'>>
>>> Pizza.get_radius is Pizza().get_radius
True
>>> Pizza.get_radius()
42
Whatever the way you use to access this method, it will be always bound to the class it is attached too, and its first argument will be the class itself (remember that classes are objects too). When to use this kind of methods? Well class methods are mostly useful for two types of methods:
- Factory methods, that are used to create an instance for a class using for example some sort of pre-processing. If we use a @staticmethod instead, we would have to hardcode the Pizza class name in our function, making any class inheriting from Pizza unable to use our factory for its own use.
class Pizza(object):
def __init__(self, ingredients):
self.ingredients = ingredients
@classmethod
def from_fridge(cls, fridge):
return cls(fridge.get_cheese() + fridge.get_vegetables())
- Static methods calling static methods: if you split a static methods in several static methods, you shouldn't hard-code the class name but use class methods. Using this way to declare ou method, the Pizza name is never directly referenced and inheritance and method overriding will work flawlessly
class Pizza(object):
def __init__(self, radius, height):
self.radius = radius
self.height = height
@staticmethod
def compute_circumference(radius):
return math.pi * (radius ** 2)
@classmethod
def compute_volume(cls, height, radius):
return height * cls.compute_circumference(radius)
def get_volume(self):
return self.compute_volume(self.height, self.radius)
Abstract methods An abstract method is a method defined in a base class, but that may not provide any implementation. In Java, it would describe the methods of an interface. So the simplest way to write an abstract method in Python is:
class Pizza(object):
def get_radius(self):
raise NotImplementedError
Any class inheriting from Pizza should implement and override the get_radius method, otherwise an exception would be raised. This particular way of implementing abstract method has a drawback. If you write a class that inherits from Pizza and forget to implement get_radius, the error will only be raised when you'll try to use that method.
>>> Pizza()
<__main__.Pizza object at 0x7fb747353d90>
>>> Pizza().get_radius()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in get_radius
NotImplementedError
There's a way to triggers this way earlier, when the object is being instantiated, using the abc module that's provided with Python.
import abc
class BasePizza(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get_radius(self):
"""Method that should do something."""
Using abc and its special class, as soon as you'll try to instantiate BasePizza or any class inheriting from it, you'll get a TypeError.
>>> BasePizza()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class BasePizza with abstract methods get_radius
Mixing static, class and abstract methods When building classes and inheritances, the time will come where you will have to mix all these methods decorators. So here's some tips about it. Keep in mind that declaring a class as being abstract, doesn't freeze the prototype of that method. That means that it must be implemented, but i can be implemented with any argument list.
import abc
class BasePizza(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get_ingredients(self):
"""Returns the ingredient list."""
class Calzone(BasePizza):
def get_ingredients(self, with_egg=False):
egg = Egg() if with_egg else None
return self.ingredients + egg
This is valid, since Calzone fulfil the interface requirement we defined for BasePizza objects. That means that we could also implement it as being a class or a static method, for example:
import abc
class BasePizza(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def get_ingredients(self):
"""Returns the ingredient list."""
class DietPizza(BasePizza):
@staticmethod
def get_ingredients():
return None
This is also correct and fulfil the contract we have with our abstract BasePizza class. The fact that the get_ingredients method don't need to know about the object to return result is an implementation detail, not a criteria to have our contract fulfilled. Therefore, you can't force an implementation of your abstract method to be a regular, class or static method, and arguably you shouldn't. Starting with Python 3 (this won't work as you would expect in Python 2, see issue5867), it's now possible to use the @staticmethod and @classmethod decorators on top of @abstractmethod:
import abc
class BasePizza(object):
__metaclass__ = abc.ABCMeta
ingredient = ['cheese']
@classmethod
@abc.abstractmethod
def get_ingredients(cls):
"""Returns the ingredient list."""
return cls.ingredients
Don't misread this: if you think this going to force your subclasses to implement get_ingredients as a class method, you are wrong. This simply implies that your implementation of get_ingredients in the BasePizza class is a class method. An implementation in an abstract method? Yes! In Python, contrary to methods in Java interfaces, you can have code in your abstract methods and call it via super():
import abc
class BasePizza(object):
__metaclass__ = abc.ABCMeta
default_ingredients = ['cheese']
@classmethod
@abc.abstractmethod
def get_ingredients(cls):
"""Returns the ingredient list."""
return cls.default_ingredients
class DietPizza(BasePizza):
def get_ingredients(self):
return ['egg'] + super(DietPizza, self).get_ingredients()
In such a case, every pizza you will build by inheriting from BasePizza will have to override the get_ingredients method, but will be able to use the default mechanism to get the ingredient list by using super().

Getting there from DebConf13 and the Debian 20th birthday
For those coming to Switzerland for
Following
I have finished preparing the beta 2 of the next release of OpenStack. It is currently only available from out Git on Alioth (in /git/openstack), and directly from my jenkins repository, which creates Wheezy backports for it:
The wave hits
A few hours after the warning email, details appeared on slashdot and sure enough, thousands of visitors started coming to this site.
As detailed previously, the site is running on
My last running update was sent after I had a great April month with
two successful trail races (35km then 44km).
The next target was Mont-Blanc marathon in Chamonix valley in early July, so it's time for me to send a new update before summer.
May and June was mostly training. Huge training. Very long distances
with 375km in May and over 430 in June (breaking my former monthly
record by over 50 kilometers). Yes, that means over 14 kilometers
every day..:-)
I indeed achieved that with my "go to work partly by running" habits,
which are now deeply included in my daily schedule. Much more
convenient than bus ride (with the typical random schedules of RATP,
which French people translate by "Rentre Avec Tes Pieds" or "Come back
home by walking"....which is exactly what I'm doing).
With such training (sometimes including extra rides in the forests,
with short but steep ups and downs)....I was well prepared for THE
early summer goal : Marathon du Mont-Blanc in Chamonix (not Ultra
Trail du Mont-blanc : this one, I'm not ready for....yet)..
My first mountain trail race, and what race! Imagine 42km in the
moutain lovers heaven, namely the Chamonix Valley, with the
Mont-Blanc, Aiguille Verte, Aiguille du Midi, Aiguilles Rouges, etc.
above your head. Of course, not only in the valley but goind up and
down "slightly"....in short 2200 meters positive climb..:-)
I ran it with a female friend of mine, who I met in local races and
through runners web forums. Sabine has about the same running skills
than me and we developed a good and nice friendship, sharing our love
for running in the nature. So, during May and June, we decided to run
this race together as we were both registered for it.
A first trial in a local 35km trail race two weeks before the MMB was
very successful. We ended up in 3h40 and Sabine was ranked 5th female
runner. And, moreover, we had a tremendous pleasure by crossing the
finishing line together.
So, we did it again in Chamonix. And, hell, we did it well: I had set
the goal to 6 hours and 30 minutes and we completed the race in..... 6
hours and 34 minutes. Much much better than I and she would have
expected. And, believe me, crossing the finishing line was again a
special moment, particularly because the crowd at the end of this race
make it look like l'Alpe d'Huez on the Tour de France. And, this on a
wonderful sunny day, arriving at Planpraz, facing the Mont-Blanc. If
you've ever been to Chamonix, you know what I mean.
So, first moutain trail and, hell, not the last one! For those of you
who can read French...or just want to see some pictures of a running
bubulle,
Since 2 weeks I'm faced with some problems at a
Present Extension Redirection
Multi-buffered applications have always behaved poorly in the presence
of the Composite extension:
For all Spanish-speakers that read my blog, specially for the cyclists among you, and most specially for those that dwell in Mexico City's streets: I was recently pointed to a project started inside the Faceboook labrynth by