Deep Analysis of TypeError in Python's super(): The Fundamental Difference Between Old-style and New-style Classes

Dec 08, 2025 · Programming · 8 views · 7.8

Keywords: Python | super() | TypeError | old-style classes | new-style classes | object-oriented programming

Abstract: This article provides an in-depth exploration of the root cause behind the TypeError: must be type, not classobj error when using Python's super() function in inheritance scenarios. By analyzing the fundamental differences between old-style and new-style classes, particularly the relationship between classes and types, and the distinction between issubclass() and isinstance() tests, it explains why HTMLParser as an old-style class causes super() to fail. The article presents correct methods for testing class inheritance, compares direct parent method calls with super() usage, and helps developers gain a deeper understanding of Python's object-oriented mechanisms.

Problem Background and Phenomenon

In Python programming, using the super() function to call parent class methods is a common inheritance pattern. However, when inheriting from certain classes, developers may encounter the TypeError: must be type, not classobj error. Consider the following code example:

>>> from HTMLParser import HTMLParser
>>> class TextParser(HTMLParser):
...     def __init__(self):
...         super(TextParser, self).__init__()
...         self.all_data = []
...
>>> TextParser()
TypeError: must be type, not classobj

This error appears to be related to old-style versus new-style classes, but the test isinstance(HTMLParser(), object) returns True, which can misleadingly suggest that HTMLParser is a new-style class. This represents a common misunderstanding of Python's class system.

The Fundamental Difference Between Old-style and New-style Classes

Python 2.x maintains a distinction between old-style and new-style classes, a division that has been unified in Python 3 (where all classes are new-style). To understand the super() error, one must deeply comprehend the essential differences between these class types.

Class Type versus Instance Type

For old-style classes, the class itself is not a type but rather a classobj:

>>> class OldStyle:
...     pass
>>> type(OldStyle)
<type 'classobj'>
>>> isinstance(OldStyle, type)
False

In contrast, for new-style classes, the class itself is a type:

>>> class NewStyle(object):
...     pass
>>> type(NewStyle)
<type 'type'>
>>> isinstance(NewStyle, type)
True

This difference directly impacts the implementation of the super() function. super() requires its parameters to be types, but old-style classes are classobj instances, thus triggering the TypeError.

Correct Testing Methods

A common misconception is using isinstance(instance, object) to test whether an instance is new-style. In reality, all Python objects are instances of object, making this test ineffective for distinguishing between old-style and new-style classes.

The correct testing approaches are:

  1. Testing whether a class is new-style: Use issubclass(cls, object)
  2. >>> issubclass(OldStyle, object)
    False
    >>> issubclass(int, object)  # int is a new-style class
    True
  3. Testing whether an instance comes from a new-style class: Use issubclass(instance.__class__, object)
  4. >>> instance = OldStyle()
    >>> issubclass(instance.__class__, object)
    False

The Reality of HTMLParser

Returning to the original problem, HTMLParser in Python 2.x is an old-style class. Although isinstance(HTMLParser(), object) returns True, this only indicates that its instances are objects, not that HTMLParser itself is a new-style class.

Verification method:

>>> from HTMLParser import HTMLParser
>>> issubclass(HTMLParser, object)
False
>>> type(HTMLParser)
<type 'classobj'>

Therefore, when TextParser inherits from HTMLParser and attempts to use super(TextParser, self).__init__(), the super() function receives a classobj instead of a type, resulting in the error.

Solutions and Alternative Approaches

Solution 1: Direct Parent Method Call

For old-style classes, the most straightforward solution is to explicitly call the parent class's initialization method:

class TextParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)  # Direct parent method call
        self.all_data = []

This approach avoids using super() and works with all class types, but loses the dynamic resolution advantages of super() in multiple inheritance scenarios.

Solution 2: Creating an Adapter Class

If super() functionality is required, one can create an intermediate class that inherits from object:

class NewStyleHTMLParser(HTMLParser, object):
    """Wraps an old-style class as new-style"""
    pass

class TextParser(NewStyleHTMLParser):
    def __init__(self):
        super(TextParser, self).__init__()  # Now works correctly
        self.all_data = []

This method converts old-style classes to new-style through multiple inheritance, but potential method resolution order (MRO) issues should be considered.

Solution 3: Upgrading to Python 3

In Python 3, where all classes are new-style, this problem naturally disappears:

# Python 3 code
from html.parser import HTMLParser

class TextParser(HTMLParser):
    def __init__(self):
        super().__init__()  # Works correctly
        self.all_data = []

Deep Understanding: Evolution of the Type System

Python's type system has evolved from old-style to new-style classes, a change that profoundly impacted the language's object-oriented capabilities:

Limitations of Old-style Classes

Major issues with old-style classes include:

Advantages of New-style Classes

New-style classes introduced a unified type model:

Practical Recommendations

  1. Explicitly inherit from object in Python 2: Always define classes using class MyClass(object): to ensure creation of new-style classes.
  2. Understand class types in third-party libraries: When using third-party libraries, especially those written for Python 2.x, be aware that their classes may be old-style.
  3. Choose appropriate calling methods: Use direct parent method calls for known old-style classes; use super() for new-style classes or when multiple inheritance support is needed.
  4. Gradually migrate to Python 3: Python 3's unified type system eliminates these historical issues and represents the long-term solution.

Conclusion

The TypeError: must be type, not classobj error raised by super() originates from the fundamental differences between Python's old-style and new-style classes. Old-style classes (like HTMLParser) are classobj instances rather than types, while super() requires type parameters. Correctly distinguishing between old-style and new-style classes requires using issubclass(cls, object) rather than isinstance(instance, object). In practical development, choose between direct parent method calls or adapter patterns based on specific circumstances, and prioritize migration to Python 3 for a more consistent type system experience.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.