At this point, we have a fairly full-featured class that generates and initializes instances, along with two new bits of behavior for processing instances in the form of methods. So far, so good.
As it stands, though, testing is still a bit less convenient than it needs to be—to trace our objects, we have to manually fetch and print individual attributes (e.g., bob.name, sue.pay). It would be nice if displaying an instance all at once actually gave us some useful information. Unfortunately, the default display format for an instance object isn’t very good—it displays the object’s class name, and its address in memory (which is essentially useless in Python, except as a unique identifier).
To see this, change the last line in the script to print(sue) so it displays the object as a whole. Here’s what you’ll get—the output says that sue is an “object” in 3.X, and an “instance” in 2.X as coded:
Bob Smith 0 Sue Jones 100000 Smith Jones <__main__.Person object at 0x00000000029A0668>
Fortunately, it’s easy to do better by employing operator overloading—coding methods in a class that intercept and process built-in operations when run on the class’s instances. Specifically, we can make use of what are probably the second most commonly used operator overloading methods in Python, after __init__: the __repr__ method we’ll deploy here, and its __str__ twin introduced in the preceding chapter.
These methods are run automatically every time an instance is converted to its print string. Because that’s what printing an object does, the net transitive effect is that printing an object displays whatever is returned by the object’s __str__ or __repr__ method, if the object either defines one itself or inherits one from a superclass. Double-underscored names are inherited just like any other.
Technically, __str__ is preferred by print and str, and __repr__ is used as a fallback for these roles and in all other contexts. Although the two can be used to implement different displays in different contexts, coding just __repr__ alone suffices to give a single display in all cases—prints, nested appearances, and interactive echoes. This still allows clients to provide an alternative display with __str__, but for limited contexts only; since this is a self-contained example, this is a moot point here.
The __init__ constructor method we’ve already coded is, strictly speaking, operator overloading too—it is run automatically at construction time to initialize a newly created instance. Constructors are so common, though, that they almost seem like a special case. More focused methods like __repr__ allow us to tap into specific operations and provide specialized behavior when our objects are used in those contexts.
Let’s put this into code. The following extends our class to give a custom display that lists attributes when our class’s instances are displayed as a whole, instead of relying on the less useful default display:
# Add __repr__ overload method for printing objects class Person: def __init__(self, name, job=None, pay=0): self.name = name self.job = job self.pay = pay def lastName(self): return self.name.split()[-1] def giveRaise(self, percent): self.pay = int(self.pay * (1 + percent)) def __repr__(self): # Added method return '[Person: %s, %s]' % (self.name, self.pay) # String to print if __name__ == '__main__': bob = Person('Bob Smith') sue = Person('Sue Jones', job='dev', pay=100000) print(bob) print(sue) print(bob.lastName(), sue.lastName()) sue.giveRaise(.10) print(sue)
Notice that we’re doing string % formatting to build the display string in __repr__ here; at the bottom, classes use built-in type objects and operations like these to get their work done. Again, everything you’ve already learned about both built-in types and functions applies to class-based code. Classes largely just add an additional layer of structure that packages functions and data together and supports extensions.
We’ve also changed our self-test code to print objects directly, instead of printing individual attributes. When run, the output is more coherent and meaningful now; the “[...]” lines are returned by our new __repr__, run automatically by print operations:
[Person: Bob Smith, 0] [Person: Sue Jones, 100000] Smith Jones [Person: Sue Jones, 110000]
Design note: as we’ll learn in Chapter 30, the __repr__ method is often used to provide an as-code low-level display of an object when present, and __str__ is reserved for more user-friendly informational displays like ours here. Sometimes classes provide both a __str__ for user-friendly displays and a __repr__ with extra details for developers to view. Because printing runs __str__ and the interactive prompt echoes results with __repr__, this can provide both target audiences with an appropriate display.
Since __repr__ applies to more display cases, including nested appearances, and because we’re not interested in displaying two different formats, the all-inclusive __repr__ is sufficient for our class. Here, this also means that our custom display will be used in 2.X if we list both bob and sue in a 3.X print call—a technically nested appearance, per the sidebar in Version Portability: Prints.