Python: Old versus New Class Declaration
As a followup to my post on Checking Python Code with Pylint, here’s a look at what the effect is of using “new style” vs “old style” declaration when defining classes in Python. The ‘pylint’ checking tool for python complains that the “old style” class declaration is a “coding violation”.
The sample code here declares OldStyleClass and NewStyleClass types:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class OldStyleClass(): '''Old style class declaration''' def __init__(self): pass def __str__(self): # Default: <__main__.OldStyleClass instance at 0x100531050> return "String rendering of OldStyleClass" def __repr__(self): return "Generic representation of OldStyleClass" class NewStyleClass(object): '''New style class declaration''' def __init__(self): pass def __str__(self): # Default: <__main__.NewStyleClass object at 0x10052aa50> return "String rendering of NewStyleClass" def __repr__(self): # Default: <__main__.NewStyleClass object at 0x10601ba90> # return 'NewStyleClass()' return "Generic representation of NewStyleClass" |
Printing some information about the old and new class types and instances of each of them:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | def main(): '''Examine old and new style classes and instances of them''' old_obj = OldStyleClass() print "\nOld Style class declaration:" print "Class type: ", ; print type(OldStyleClass) print "Instance type: ", ; print type(old_obj) print "Instance id: ", ; print '0x' + hex(id(old_obj)) print "String rendering: ", ; print str(old_obj) print "Attributes of the object: " for name in dir(old_obj): print name new_obj = NewStyleClass() print "\nNew Style class declaration:" print "Class type: ", ; print type(NewStyleClass) print "Instance type: ", ; print type(new_obj) ## , hex(id(new_obj)) print "Instance id: ", ; print '0x' + hex(id(new_obj)) print "String rendering: ", ; print str(new_obj) print "Attributes of the object: " for name in dir(new_obj): print name print "\nNames defined in 'object' class:" for name in dir(object): print name if __name__ == '__main__': main() |
Note the use of dir(): it returns a list of attribute names, all on one line by default, printing with “for…print” makes it more readable:
for name in dir(old_obj): print name |
The major differences:
- Class type is “<type ‘type’>” for new style vs “<type ‘classobj’>” for old style
- Instance type is “<class ‘__main__.NewStyleClass’>” vs “<type ‘instance’>” for old style
For new style object declaration, the instance type lists the class name: useful for debugging.
String rendering is the same for both, defaults to “<__main__.OldStyleClass instance at 0x100531050>”, can be overridden by implementing a __str__() method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | Old Style class declaration: Class type: <type 'classobj'> Instance type: <type 'instance'> Instance id: 0x0x10d5f8320 String rendering: String rendering of OldStyleClass Attributes of the object: __doc__ __init__ __module__ __repr__ __str__ New Style class declaration: Class type: <type 'type'> Instance type: <class '__main__.NewStyleClass'> Instance id: 0x0x10d5f2ad0 String rendering: String rendering of NewStyleClass Attributes of the object: __class__ __delattr__ __dict__ __doc__ __format__ __getattribute__ __hash__ __init__ __module__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __sizeof__ __str__ __subclasshook__ __weakref__ |
There are a lot of new methods supported in a class defined in the new style, basically default implementations in the “object” class that can be selectively overridden in a class that extends it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Names defined in 'object' class: __class__ __delattr__ __doc__ __format__ __getattribute__ __hash__ __init__ __new__ __reduce__ __reduce_ex__ __repr__ __setattr__ __sizeof__ __str__ __subclasshook__ |
What’s the difference between __str__() and __repr__()? If both are implemented (as in NewStyleClass above), each will be invoked in different contexts:
1 2 3 4 5 6 7 8 | $ python >>> from oldnewclass import NewStyleClass >>> NewStyleClass() Generic representation of NewStyleClass >>> str(NewStyleClass()) 'String rendering of NewStyleClass' >>> [NewStyleClass() for i in range(0,3)] [Generic representation of NewStyleClass, Generic representation of NewStyleClass, Generic representation of NewStyleClass] |
The interpreter will get the __repr__() result when printing the value of an expression, but it will print the return value of __str__() if the expression is explicitly coerced to a string value with str(). The __repr__() value will also be printed for an array of values (the list comprehension in line 5 above).
Python documentation says that the __repr__() method should return a string rendering of a Python expression that can recreate a new instance of the object (like the ‘return “NewStyleClass()”‘ that’s currently commented out in NewStyleClass.__repr__() above).
Versions
$ python -V
Python 2.7.10
References
3.3 New-style and classic classes
Unifying types and classes in Python 2.2 | Python.org (paper by Guido van Rossum)
inheritance – How to avoid Pylint warnings for constructor of inherited class in Python 3?
inheritance – Should all Python classes extend object?