Search code examples
pythonsubclassfinal

How does python prevent a class from being subclassed?


I came across the following in the python docs:

bool([x])

Convert a value to a Boolean, using the standard truth testing procedure. If x is false or omitted, this returns False; otherwise it returns True. bool is also a class, which is a subclass of int. Class bool cannot be subclassed further. Its only instances are False and True.

I've never in my life wanted to subclass bool, but naturally I immediately tried it, and sure enough:

>>> class Bool(bool):
    pass

Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    class Bool(bool):
TypeError: Error when calling the metaclass bases
    type 'bool' is not an acceptable base type

So, the question: How is this done? And can I apply the same technique (or a different one) to mark my own classes as final, i.e., to keep them from being subclassed?


Solution

  • The bool type is defined in C, and its tp_flags slot deliberately does not include the Py_TPFLAGS_BASETYPE flag.

    C types need to mark themselves explicitly as subclassable.

    To do this for custom Python classes, use a metaclass:

    class Final(type):
        def __new__(cls, name, bases, classdict):
            for b in bases:
                if isinstance(b, Final):
                    raise TypeError("type '{0}' is not an acceptable base type".format(b.__name__))
            return type.__new__(cls, name, bases, dict(classdict))
    
    class Foo:
        __metaclass__ = Final
    
    class Bar(Foo):
        pass
    

    gives:

    >>> class Bar(Foo):
    ...     pass
    ... 
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 5, in __new__
    TypeError: type 'Foo' is not an acceptable base type