A style guide to static, class, and abstract methods in Python 2.X and 3.X

This guide documents one code style of static, class, and abstract methods in Python. Following this style, your code can be run in both Python 2.X and Python 3.X.

This guide is a reference for creating new code. Since it covers a single way to define these methods and ignores many others, it should not be used to review existent code.

This guide can also be a mini tutorial of how to use these OOP concepts in Python, given that you already know OOP and can code some simple Python classes.

In the following, I will first describe the instance method as a prelude. Then I present the style for static methods, class methods, and abstract methods one by one. Each concept will be accompanied with examples.

Instance methods

Instance methods are the majority of methods you code and use. The work method in the following code is an instance method.

class Employee:
    def __init__(self, name):
        self.name = name
    def work(self):
        print(self.name + " is working.")

You can call it with an instance Employee("Alice").work(), but you cannot call it with the class name Employee.work(), unless you explicitly add the instance as an argument Employee.work(Employee("Alice")).

Static methods

Unlike instance methods, which are bound to instances, static methods belong to their classes. They are used to manipulate static variables shared across all instances.

For example, the following code uses number_of_employees to track the number of employees and uses the static method report to report it. It does not have the self parameter.

class Employee:
    number_of_employees = 0
    def __init__(self, name):
        self.name = name
        Employee.number_of_employees += 1
    def work(self):
        print(self.name + " is working.")
    @staticmethod
    def report():
        print(str(Employee.number_of_employees) + " employees are working.")

You can call the static method with either the class name Employee.report() or an instance Employee("Alice").report().

Since the class name Employee is hard-coded in the report method, the subclasses of Employee will report the same number when calling the report method.

class Employee:
    number_of_employees = 0
    def __init__(self, name):
        self.name = name
        Employee.number_of_employees += 1
    def work(self):
        print(self.name + " is working.")
    @staticmethod
    def report():
        print(str(Employee.number_of_employees) + " employees are working.")

class Manager(Employee):
    pass

When you call Manager.report() or Manager("Bob").report(), it still report the total number of employees instead of the number of managers. To let it report the number of managers, you need to use the class methods described in the next section.

Class methods

Class methods are similar to static methods in that they also belong to the class instead of the instances, but they are more powerful than static methods.

The following class method report takes an extra parameter cls, which transfers the class argument.

class Employee:
    number = 0
    def __init__(self, name):
        self.name = name
        Employee.number += 1
    def work(self):
        print(self.name + " is working.")
    @classmethod
    def report(cls):
        print(str(cls.number) + " " + cls.__name__ + "s are working.")

When you call it with the class name Employee.report() or an instance Employee("Alice").report(), the class Employee will be transferred to cls in both cases.

This mechanism allows us to create separate counters for both the base class and the subclasses as in the following code.

class Employee:
    number = 0
    def __init__(self, name):
        self.name = name
        Employee.number += 1
    def work(self):
        print(self.name + " is working.")
    @classmethod
    def report(cls):
        print(str(cls.number) + " " + cls.__name__ + "s are working.")

class Manager(Employee):
    number = 0
    def __init__(self, name):
        Manager.number += 1
        Employee.__init__(self, name)

When you call report with Manager or its instances, the number of managers will be reported instead of the one of the total employees.

Abstract methods

Python’s abstract methods are like C++’s pure virtual functions: they define methods that must be implemented in the subclasses, and classes containing abstract methods (aka abstract classes) cannot be instantiated.

The following example shows the skeleton of an abstract method setup.

from abc import  ABCMeta, abstractmethod

class Super(metaclass=ABCMeta):
    @abstractmethod
    def method(self, ...):
        pass

Reference

Learning Python, 5th Edition: Mark Lutz

Written on April 27, 2019