Methods for Both Classes and Instances in Python

0.00 avg. rating (0% score) - 0 votes

I came across a situation where it would be nice for a Python class to have a method which works for both the class and its instances, and when called in an instance context, to know on which instance it was invoked. Python does not support this directly, but as it is a very flexible language, it can be done. To make it work somewhat nicely requires just a few lines of not so obvious code.

To have Python call a method in the context of a class requires the use of the decorator @classmethod. You can call such a method for an instance as well, but the method is only passed the class as an argument, not the instance.

Here’s a an example:

The class has one classmethod which simply prints what it knows about the world around it. It knows the class and the arguments passed to it. When invoked on a class instance, it does not know which instance it was invoked on.

What I needed was to have self defined when invoked in an instance context. I solved it by injecting self into the classmethod when the class constructor is run at instantiation time. This requires a slight modification to the classmethod’s signature, and replacing the original classmethod with an instancemethod which takes both the class as a positional argument and the instance as a keyword argument. This is achieved nicely with a utility function called class2instancemethod:

There are four things to note here. The first one is the class2instancemethod utility function which takes a classmethod and a class instance as argument and returns an instancemethod which has class (“cls”) as the first positional parameter and instance (“self”) as a keyword argument.

The second interesting thing is the class constructor which calls our utility function at class instantiation time and replaces the original classmethod with our patched version.

The third interesting thing is the signature of the classmethod m:

Argument list now contains self as a keyword argument. When invoked in a class context, self will be None. When invoked in an instance context, self will receive the instance.

The fourth thing to note is the print line:

We now have a self to print. Here’s what looks like now:

So now it is possible to have the classmethod know the instance when called in an instance context.

With this implementation we would have to patch every method in our constructor method, which is repetitive and error-prone.

We can improve the situation by making our own decorator for such methods, and a helper function which needs to be called just once in the constructor. Here’s the full code:

And here’s an example on how to use it:

Example output:

This makes it quite easy to have classmethods which are able to get the target object when invoked on an instance.

The only tricky thing is to have the arguments in the right order. The order should be this:

  1. cls
  2. any positional arguments
  3. *args
  4. any named keyword arguments you wish to be able to use also as positional ones (when not using *args)
  5. self=None
  6. *kwargs

Only cls and self=None are required, the rest are optional.

As a final note I would like to point out that this has only been tested with Python 3.5.

Leave a Reply