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'>)
FeatureOld-styleNew-style
type(instance)instanceThe class itself
__mro__AbsentCooperative method resolution order
super()Broken with diamondsWorks correctly
Properties, __slots__UnavailableAvailable
Descriptors, __getattribute__IgnoredHonoured
type(instance) == ClassFalseTrue

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-argument super() only works in Python 3.
  • Always inherit from object when 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.

Lists, dicts and the Python 2-only APIs Migrating to Python 3

Last refreshed 2026-09-18.