Python 3 drops support for “old-style” classes, and introduces dedicated syntax for metaclasses. Read on for details.
- Fixer: None
- Prevalence: Very common
Python 2 had two styles of classes: “old-style” and “new-style”.
Old-style classes were defined without a superclass (or by deriving from other old-style classes):
class OldStyle: pass class OldStyleChild(OldStyle): pass
New-style classes derive from a built-in class – in most cases,
class NewStyle(object): pass class NewInt(int): pass
In Python 3, all classes are new-style:
object is the default superclass.
For code compatible across Python versions, all classes should be defined with
explicit superclasses: add
(object) to all class definitions with
no superclass list.
To find all places to change, you can run the following command over
grep --perl 'class\s+[a-zA-Z_]+:'
However, you will need to test the result thoroughly. Old- and new-style classes have slightly differend semantics, described below.
From a developer’s point of view, the main difference between the two is method resolution in multiple inheritance chains. This means that if your code uses multiple inheritance, there can be differences between which method is used for a particular subclass.
Object model details¶
Another difference is in the behavior of arithmetic operations:
in old-style classes, operators like
% generally coerced both
operands to the same type.
In new-style classes, instead of coercion, several special methods
__radd__) may be tried to arrive at the result.
python-modernize -wnf libmodernize.fixes.fix_metaclass
- Prevalence: Rare
For metaclasses, Python 2 uses a specially named class attribute:
class Foo(Parent): __metaclass__ = Meta
In Python 3, metaclasses are more powerful, but the metaclass needs to be known before the body of the class statement is executed. For this reason, metaclasses are now specified with a keyword argument:
class Foo(Parent, metaclass=Meta): ...
The new style is not compatible with Python 2 syntax.
However, the Compatibility library: six library provides a workaround that works in both
versions – a base class named
This workaround does a bit of magic to ensure that the result is the same
as if a metaclass was specified normally:
import six class Foo(six.with_metaclass(Meta, Parent)): pass
The recommended fixer will import
six and add
quite reliably, but do test that the result still works.