Summary
Unlike many other languages, Python allows abstract static methods (see the reference).
A class directly or indirectly deriving from abc.ABC cannot be instantiated, unless all its abstract methods are redefined. This includes static methods. It is possible to call an “abstract” static method, but an attempt to instantiate a class containing it leads to a TypeError.
Example
from abc import abstractmethod, ABC
class A(ABC):
@staticmethod
@abstractmethod
def static_method():
print("A.static_method()")
A.static_method() # prints A.static_method()
obj = A() # raises TypeError: Can't instantiate abstract class A
# without an implementation for abstract method 'static_method'
Some Mechanics
In Python, @abstractmethod is a decorator, not a keyword. It is defined in a library as follows:
def abstractmethod(funcobj):
funcobj.__isabstractmethod__ = True
return funcobj
Any function or class can be marked abstract
@abstractmethod is a regular decorator that can technically be applied to any function or class. It is only checked by the constructor of abc.ABC. Any occurrences of @abstractmethod outside of classes descending from abc.ABC are ignored.
All of the following is legal in Python as of version 3.13, although it is confusing and not recommended for production use:
from abc import abstractmethod
# don't do this in production
@abstractmethod
def my_func():
@abstractmethod
def inner_func():
pass
@abstractmethod
class Foo:
pass
It only matters, if ABC is involved
You can create an instance of a class with an abstract method, as long as it is not a descendant of abc.ABC:
from abc import abstractmethod
class Base: # does not derive from abc.ABC
@abstractmethod
def method(self):
pass
obj = Base() # works; it would fail only if Base derives from abc.ABC
Classes can be abstract methods too
It is possible to decorate classes with @abstractmethod, even though it is probably not the intended use. Having an abstract inner class in a class descended from abc.ABC will trigger an exception upon instantiation. The error message still says you need to implement a “method”:
from abc import abstractmethod, ABC
class AbstractBase(ABC):
@abstractmethod
class Inner: # don't do this in production
pass
obj = AbstractBase() # TypeError: Can't instantiate abstract class AbstractBase
# without an implementation for abstract method 'Inner'
You can override an abstract method with anything
from abc import abstractmethod, ABC
class Base(ABC):
@abstractmethod
def method(self):
pass
# don't do this in production
class DerivedA(Base):
def method(self, arg1, arg2): # wrong number of arguments
pass
class DerivedB(Base):
@staticmethod
def method(): # static method overriding a regular method
pass
class DerivedC(Base):
method = 10 # integer field overriding an abstract method
class Derivedd(Base):
class method: # inner class overriding an abstract method
pass
monsters = [DerivedA(), DerivedB(), DerivedC(), DerivedD()] # all cool, no errors
Calling abstract methods is OK
Abstract methods can have implementations. It is possible to call an abstract static method:
from abc import abstractmethod, ABC
class AbstractBase(ABC):
@staticmethod
@abstractmethod
def abstract_static_method():
print("method()")
AbstractBase.abstract_static_method() # prints "method()"
obj = AbstractBase() # raises TypeError
Are abstract static methods useful?
Abstract static methods make zero sense in classic OOP, but the fact that an example of such method is included in the Python documentation means that this feature is intentional. Compare it to the “abstract inner class” feature, that is missing from the documentation, and also produces a weird error message.
One take on it is that this allows to make static methods part of a derived class contract: the derived class is not “complete” unless it implements these static methods.
As far as I know, it is not possible to specify such contract in Java or C#. In C++, we can achieve similar behavior using concepts:
// C++: concept to check for static method
template <typename T>
concept HasStaticMethod = requires {
{ T::my_static_method() } -> std::same_as<void>;
};
template <HasStaticMethod T>
class DoSomething {
...
};
Class DoSometing won’t accept parameter T unless it defines a static method void my_static_method().
In conclusion
Python’s “ask for forgiveness, not permission” philosophy fully applies to its abstract methods. They are implemented by a library feature that can be abused in many different ways, and is not really strongly enforced. Sometimes it can lead to interesting creative solutions, but oftentimes it leads to confusing and outright incorrect code. But that’s the nature Python: what other languages consider a sin, in Python is forgiven by default.
