What Does `(object)` Do Next to a Class Name in Python? Differences, Subclasses & Best Practices

If you’ve spent time reading Python code, you may have noticed class definitions written in two styles:

class MyClass:  # Style 1
    pass
 
class MyClass(object):  # Style 2
    pass

The presence of (object) in the second example is not arbitrary—it reflects a critical distinction in Python’s class system: old-style classes vs. new-style classes. This blog demystifies what (object) does, why it matters, and how it impacts inheritance, subclassing, and best practices in Python.

Table of Contents#

  1. What is (object) in a Class Definition?
  2. Old-Style vs. New-Style Classes: Core Differences
  3. Subclasses: How (object) Affects Child Classes
  4. Best Practices: When to Use (object)
  5. Conclusion
  6. References

What is (object) in a Class Definition?#

In Python, object is the base class for all new-style classes. When you define a class with class MyClass(object):, you explicitly declare it as a new-style class, inheriting from object.

Key Background: Old-Style vs. New-Style Classes#

  • Old-style classes (or "classic classes") were the original class model in Python 2. They lacked many modern features and were defined without inheriting from object (e.g., class MyClass:).
  • New-style classes were introduced in Python 2.2 (PEP 253) to unify classes and types. They inherit from object and provide advanced features like method resolution order (MRO), descriptors, and super().

In Python 3, all classes are new-style by default, even if (object) is omitted. This means class MyClass: and class MyClass(object): are identical in Python 3. However, in Python 2, the distinction is critical.

Old-Style vs. New-Style Classes: Core Differences#

To understand why (object) matters, let’s explore the key differences between old-style and new-style classes.

1. Inheritance Hierarchy#

  • New-style classes form a single inheritance tree with object as the root. Every new-style class (and its instances) is a subclass of object.
  • Old-style classes (Python 2 only) have a separate hierarchy. They do not inherit from object, and their type is classobj (instead of type, the default metaclass for new-style classes).

Example (Python 2):

# Old-style class (no inheritance from object)
class OldStyleClass:
    pass
 
# New-style class (explicitly inherits from object)
class NewStyleClass(object):
    pass
 
print(type(OldStyleClass))   # Output: <type 'classobj'> (old-style)
print(type(NewStyleClass))   # Output: <type 'type'> (new-style, uses 'type' metaclass)

In Python 3, all classes use type as their metaclass, so type(OldStyleClass) would return <class 'type'> even without (object).

2. Method Resolution Order (MRO)#

MRO determines the order in which Python searches for methods in inherited classes. New-style classes use a C3 linearization algorithm (explicit via __mro__), while old-style classes use a simple depth-first, left-to-right order, which can cause unexpected behavior in complex inheritance (e.g., diamond patterns).

Example: Diamond Inheritance

# Python 3 (new-style by default)
class A:
    def method(self):
        print("A")
 
class B(A):
    def method(self):
        print("B")
        super().method()
 
class C(A):
    def method(self):
        print("C")
        super().method()
 
class D(B, C):
    def method(self):
        print("D")
        super().method()
 
D().method()
# Output: D → B → C → A (C3 linearization)
print(D.__mro__)  # Output: (<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)

In Python 2 with old-style classes, the MRO would be depth-first: D → B → A → C, skipping C’s method entirely (since A is reached first via B). This is why new-style MRO is preferred for predictable behavior.

3. Built-in Attributes#

New-style classes include critical attributes like __class__ (on instances) and __mro__ (on classes) that old-style classes lack.

Example: __class__ and __mro__

# Python 2 (old-style class lacks __class__ on instances)
class OldStyle:
    pass
 
obj = OldStyle()
try:
    print(obj.__class__)  # Old-style instances have no __class__ attribute
except AttributeError:
    print("Old-style instance has no __class__")  # Output: This line runs
 
# New-style class (Python 2 or 3)
class NewStyle(object):
    pass
 
obj = NewStyle()
print(obj.__class__)  # Output: <class '__main__.NewStyle'> (Python 3) or <class '__main__.NewStyle'> (Python 2 new-style)
print(NewStyle.__mro__)  # Output: (<class '__main__.NewStyle'>, <class 'object'>)

4. Descriptors, Properties, and super()#

New-style classes fully support advanced features like:

  • Descriptors: Protocols for defining attributes (e.g., __get__, __set__ methods).
  • Properties: Managed attributes via the @property decorator.
  • super(): A function to call methods from parent classes, which relies on MRO for correctness.

Old-style classes (Python 2) have limited or broken support for these features.

Example: super() with New-Style Classes

class Parent(object):
    def greet(self):
        print("Hello from Parent")
 
class Child(Parent):
    def greet(self):
        super().greet()  # Calls Parent.greet() using MRO
        print("Hello from Child")
 
Child().greet()
# Output:
# Hello from Parent
# Hello from Child

In Python 2 old-style classes, super() raises a TypeError because it requires a new-style class.

5. Metaclasses#

Metaclasses define the behavior of classes themselves (e.g., validating attributes or modifying class creation). New-style classes use type as their default metaclass, enabling custom metaclasses. Old-style classes (Python 2) do not support metaclasses in the same way.

Subclasses: When (object) Isn’t Explicitly Needed#

A subclass inherits its "style" (old or new) from its parent class.

In Python 3:#

All classes are new-style, so subclasses are new-style even if (object) is omitted:

class Parent:  # Implicitly new-style (inherits from object)
    pass
 
class Child(Parent):  # Child is new-style (inherits from Parent, which is new-style)
    pass
 
print(Child.__mro__)  # Output: (<class '__main__.Child'>, <class '__main__.Parent'>, <class 'object'>)

In Python 2:#

  • If the parent is a new-style class, the child is new-style (even without (object)).
  • If the parent is an old-style class, the child is old-style unless it explicitly inherits from object.

Example (Python 2):

class OldStyleParent:  # Old-style
    pass
 
class ChildOfOld(OldStyleParent):  # Child is old-style (inherits parent's style)
    pass
 
class NewStyleParent(object):  # New-style
    pass
 
class ChildOfNew(NewStyleParent):  # Child is new-style (inherits parent's style)
    pass
 
print(type(ChildOfOld))   # Output: <type 'classobj'> (old-style)
print(type(ChildOfNew))   # Output: <type 'type'> (new-style)

Best Practices#

1. Python 3: Omit (object) (It’s Implicit)#

In Python 3, all classes are new-style by default. Writing class MyClass(object): is redundant, as object is implicitly the base class. Most modern Python code omits (object) for brevity.

Preferred:

class MyClass:  # Implicitly inherits from object (Python 3)
    pass

2. Python 2: Always Inherit from object#

If you’re maintaining Python 2 code (though Python 2 is end-of-life), explicitly inherit from object to ensure new-style features like MRO, super(), and descriptors work correctly.

Preferred (Python 2):

class MyClass(object):  # Explicitly new-style
    pass

3. Consistency in Codebases#

If your team prefers including (object) for clarity (e.g., to explicitly signal "this is a class"), stick to that convention consistently. Avoid mixing styles (e.g., some classes with (object), others without).

4. Avoid Old-Style Classes Entirely#

Old-style classes are obsolete and unsupported in Python 3. If migrating Python 2 code, ensure all classes inherit from object to avoid subtle bugs (e.g., broken MRO or missing attributes).

Conclusion#

The (object) syntax in Python class definitions is a relic of the transition from old-style to new-style classes. In Python 3, it’s redundant because all classes implicitly inherit from object. In Python 2, however, it’s critical for enabling new-style features like MRO, super(), and descriptors.

Key takeaways:

  • (object) declares a class as "new-style," inheriting from Python’s root object class.
  • New-style classes offer superior features: predictable MRO, super(), descriptors, and object-based inheritance.
  • In Python 3, omit (object); in Python 2 (legacy code), always include it.

By understanding this distinction, you’ll write cleaner, more maintainable code and avoid pitfalls in inheritance and class behavior.

References#