Learning Python - Mark Lutz [395]
x.name = 'Bob'
y.name = 'Sue' # Fails
y.age = 30
x.age = 40 # Fails
In fact, this is a first-cut solution for an implementation of attribute privacy in Python (i.e., disallowing changes to attribute names outside a class). Although Python doesn’t support private declarations per se, techniques like this can emulate much of their purpose. This is a partial solution, though; to make it more effective, it must be augmented to allow subclasses to set private attributes more naturally, too, and to use __getattr__ and a wrapper (sometimes called a proxy) class to check for private attribute fetches.
We’ll postpone a more complete solution to attribute privacy until Chapter 38, where we’ll use class decorators to intercept and validate attributes more generally. Even though privacy can be emulated this way, though, it almost never is in practice. Python programmers are able to write large OOP frameworks and applications without private declarations—an interesting finding about access controls in general that is beyond the scope of our purposes here.
Catching attribute references and assignments is generally a useful technique; it supports delegation, a design technique that allows controller objects to wrap up embedded objects, add new behaviors, and route other operations back to the wrapped objects (more on delegation and wrapper classes in Chapter 30).
String Representation: __repr__ and __str__
The next example exercises the __init__ constructor and the __add__ overload method, both of which we’ve already seen, as well as defining a __repr__ method that returns a string representation for instances. String formatting is used to convert the managed self.data object to a string. If defined, __repr__ (or its sibling, __str__) is called automatically when class instances are printed or converted to strings. These methods allow you to define a better display format for your objects than the default instance display.
The default display of instance objects is neither useful nor pretty:
>>> class adder:
... def __init__(self, value=0):
... self.data = value # Initialize data
... def __add__(self, other):
... self.data += other # Add other in-place (bad!)
...
>>> x = adder() # Default displays
>>> print(x)
<__main__.adder object at 0x025D66B0>
>>> x
<__main__.adder object at 0x025D66B0>
But coding or inheriting string representation methods allows us to customize the display:
>>> class addrepr(adder): # Inherit __init__, __add__
... def __repr__(self): # Add string representation
... return 'addrepr(%s)' % self.data # Convert to as-code string
...
>>> x = addrepr(2) # Runs __init__
>>> x + 1 # Runs __add__
>>> x # Runs __repr__
addrepr(3)
>>> print(x) # Runs __repr__
addrepr(3)
>>> str(x), repr(x) # Runs __repr__ for both
('addrepr(3)', 'addrepr(3)')
So why two display methods? Mostly, to support different audiences. In full detail:
__str__ is tried first for the print operation and the str built-in function (the internal equivalent of which print runs). It generally should return a user-friendly display.
__repr__ is used in all other contexts: for interactive echoes, the repr function, and nested appearances, as well as by print and str if no __str__ is present. It should generally return an as-code string that could be used to re-create the object, or a detailed display for developers.
In a nutshell, __repr__ is used everywhere, except by print and str when a __str__ is defined. Note, however, that while printing falls back on __repr__ if no __str__ is defined, the inverse is not true—other contexts, such as interactive echoes, use __repr__ only and don’t try __str__ at all:
>>> class addstr(adder):
... def __str__(self): # __str__ but no __repr__
... return '[Value: %s]' % self.data # Convert to nice string
...
>>> x = addstr(3)
>>> x + 1
>>> x # Default __repr__
<__main__.addstr object at 0x00B35EF0>
>>> print(x) # Runs __str__
[Value: 4]
>>> str(x), repr(x)
('[Value: 4]', '<__main__.addstr object at 0x00B35EF0>')
Because of this, __repr__ may be best if