Old-style vs new-style classes and metaclasses
Why class Foo: and class Foo(object): behaved differently, how metaclasses were set, and what Python 3 unified.
The two object models
# Python 2 only
class Old:
pass
class New(object):
pass
print type(Old) # <type 'classobj'>
print type(New) # <type 'type'>
print Old.__mro__ # AttributeError: no MRO
print New.__mro__ # (<class 'New'>, <class 'object'>)| Feature | Old-style | New-style |
|---|---|---|
type(instance) | instance | The class itself |
__mro__ | Absent | Cooperative method resolution order |
super() | Broken with diamonds | Works correctly |
Properties, __slots__ | Unavailable | Available |
Descriptors, __getattribute__ | Ignored | Honoured |
type(instance) == Class | False | True |
Old-style classes existed for backward compatibility with Python 1.x. In Python 3 every class is new-style, so class Foo: and class Foo(object): are identical and the (object) base is unnecessary.
Diamond inheritance
# Python 2, new-style: the C3 linearisation decides which method wins
class Base(object):
def describe(self):
return "base"
class A(Base):
def describe(self):
return "A -> " + super(A, self).describe()
class B(Base):
def describe(self):
return "B -> " + super(B, self).describe()
class C(A, B):
pass
print C().describe() # A -> B -> base
print [c.__name__ for c in C.__mro__]- With old-style classes the same hierarchy resolves depth-first and the result differs, which is why mixing the two models in one hierarchy is so confusing.
- Python 2 requires the two-argument form
super(A, self); the zero-argumentsuper()only works in Python 3. - Always inherit from
objectwhen writing 2.7 code that might be ported, and put it in every class, not just the root.
Metaclasses
# Python 2: a module-level attribute on the class body
class Register(object):
__metaclass__ = RegistryMeta
name = "plugin"
# Python 3: a keyword argument in the class statement
class Register(metaclass=RegistryMeta):
name = "plugin"In Python 2 the metaclass was inherited from the base class, so a __metaclass__ attribute in a module made every class in that module new-style with that metaclass. This applies to the whole module, which is exactly as surprising as it sounds.
⚠️
Metaclasses are one of the hardest parts of a 2-to-3 port. The syntax moved and the inheritance rule changed, so an automated tool may miss a class whose metaclass came from an ancestor. Search for
__metaclass__ by hand and verify each site.FAQ
How can I tell whether a class is old-style at runtime?
In Python 2,
isinstance(MyClass, type) is False for an old-style class and True for a new-style one. In Python 3 every class passes that test.Do I need to add (object) to every class in 2.7?
Yes, if you want properties, descriptors, working
super() and correct MRO. The cost is a few characters and the payoff is that the port to Python 3 becomes a syntax change rather than a behavioural one.Related
Lists, dicts and the Python 2-only APIs Migrating to Python 3
Last refreshed 2026-09-18.