Keywords: Python | super() function | old-style vs. new-style classes | TypeError | class inheritance
Abstract: This article explores the common Python error: TypeError "argument 1 must be type, not classobj" when using the super() function. By analyzing the differences between old-style and new-style classes, it explains that the root cause is a parent class not inheriting from object, resulting in a classobj type instead of type. Two solutions are detailed: converting the parent to a new-style class (inheriting from object) or using multiple inheritance techniques. Code examples compare the types of old and new-style classes, and changes in Python 3.x are discussed. The goal is to help developers understand Python class inheritance mechanisms, avoid similar errors, and improve code quality.
Background and Error Phenomenon
In Python object-oriented programming, the super() function is commonly used to call parent class methods for cooperative multiple inheritance. However, developers may encounter the error: TypeError: super() argument 1 must be type, not classobj. This typically occurs when attempting to use super() to invoke a parent method, as shown in the following example code:
class B:
def meth(self, arg):
print arg
class C(B):
def meth(self, arg):
super(C, self).meth(arg)
print C().meth(1)Executing this code triggers the error, with traceback pointing to the line super(C, self).meth(arg). The error message clearly states that the first argument to super() must be of type type, but a classobj was provided instead.
Root Cause: Old-style vs. New-style Classes
The fundamental cause of this error lies in the two class types in Python: old-style classes (classic classes) and new-style classes. In Python 2.x, if a class is defined without explicitly inheriting from object, it is considered an old-style class with type classobj; classes inheriting from object are new-style classes with type type. The super() function requires its first argument to be of type type, so when the parent class is old-style, a type error is raised.
The following code demonstrates the type difference between old and new-style classes:
class OldStyle:
pass
class NewStyle(object):
pass
print type(OldStyle) # Output: <type 'classobj'>
print type(NewStyle) # Output: <type 'type'>In the example, class B does not inherit from object, making it an old-style class with type classobj. When subclass C tries to call the parent method using super(C, self), the super() function detects that the type of C's parent B is invalid, thus throwing the error.
Solution 1: Convert Parent to New-style Class
The most straightforward solution is to modify the parent class definition to inherit from object, converting it to a new-style class. The revised code is as follows:
class B(object):
def meth(self, arg):
print arg
class C(B):
def meth(self, arg):
super(C, self).meth(arg)
print C().meth(1) # Output: 1By changing class B: to class B(object):, class B becomes type type, allowing the super() function to work correctly. This method is simple and effective, recommended for resolving such issues. In Python programming, developing the habit of explicitly inheriting all classes from object can prevent many compatibility problems.
Solution 2: Use Multiple Inheritance Technique
If the parent class definition cannot be modified (e.g., it comes from a third-party library or legacy code), the problem can be indirectly solved using multiple inheritance. Specifically, make the subclass inherit from both the parent and object to ensure super() receives the correct type. Example code:
class B:
def meth(self, arg):
print arg
class C(B, object):
def meth(self, arg):
super(C, self).meth(arg)
print C().meth(1) # Output: 1Here, class C inherits from B and object, making C a new-style class with type type. When super(C, self) is called, the super() function correctly identifies the type and invokes the parent method. While this approach works, it may introduce complex inheritance relationships and should be used with caution.
Changes in Python 3.x
In Python 3.x, all classes are new-style, even if defined without explicitly inheriting from object. This means the above error does not occur in Python 3.x, as class types are always type. For example, the following code runs successfully in Python 3.x:
class B:
def meth(self, arg):
print(arg)
class C(B):
def meth(self, arg):
super().meth(arg)
print(C().meth(1)) # Output: 1Python 3.x also simplifies super() syntax, allowing calls without arguments (e.g., super().meth(arg)), further enhancing code conciseness. However, when maintaining or migrating Python 2.x code, it is essential to be aware of the differences between old and new-style classes to ensure compatibility.
Summary and Best Practices
The TypeError: argument 1 must be type, not classobj error with super() highlights a key distinction between old and new-style classes in Python 2.x. Old-style classes (type classobj) do not support advanced inheritance features like super(), while new-style classes (type type) provide full object-oriented capabilities. To avoid such errors, it is recommended to:
- In Python 2.x, always explicitly inherit classes from
objectto define them as new-style. - If the parent class cannot be modified, consider multiple inheritance as a temporary solution, but evaluate its design implications.
- Upgrading to Python 3.x can eliminate this issue entirely while benefiting from modern syntax features.
By understanding these core concepts, developers can leverage Python's inheritance mechanisms more effectively, writing robust and maintainable code. The examples and analysis in this article aim to provide practical guidance, helping readers master the super() function and related class inheritance knowledge.