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?

Tags:

Add a Comment