Exercise 5.3

Objectives:

  • Learn how to customize the behavior of objects by redefining special methods.

  • Change the way that user-defined objects get printed

  • Try some experiments with getattr()

Files Created: None

Files Modified: stock.py, tableformat.py

(a) Better output for printing objects

All Python objects have two string representations. The first representation is created by string conversion via str() (which is called by print). The string representation is usually a nicely formatted version of the object meant for humans. The second representation is a code representation of the object created by repr() (or simply by viewing a value in the interactive shell). The code representation typically shows you the code that you have to type to get the object.

The two representations of an object are often different. For example, you can see the difference by trying the following:

>>> s = 'Hello\nWorld'
>>> print str(s)        # Notice nice output (no quotes)
Hello
World
>>> print repr(s)       # Notice the added quotes and escape codes
'Hello\nWorld'
>>> print '%r' % s      # Alternate way to get repr() string
'Hello\nWorld'
>>>

Both kinds of string conversions can be redefined in a class if it defines the __str__() and __repr__() methods.

Modify the Stock object that you defined in Exercise 5.1 so that the __repr__() methods produce more useful output. For example:

>>> goog = Stock('GOOG', 100, 490.1)
>>> goog
Stock('GOOG', 100, 490.1)
>>>

See what happens when you read a portfolio of stocks and view the resulting list after you have made these changes. For example:

>>> import stock
>>> portfolio = stock.read_portfolio('Data/portfolio.csv')
>>> portfolio
... see what the output is ...
>>>

(b) Defining a custom exception

It is often good practice for libraries to define their own exceptions. This makes it easier to distinguish between Python exceptions raised in response to common programming errors versus exceptions intentionally raised by a library to a signal a specific usage problem. Modify the create_formatter() function from the last exercise so that it raises a custom FormatError exception when the user provides a bad format name. For example:

>>> from tableformat import create_formatter
>>> formatter = create_formatter('xls')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "tableformat.py", line 71, in create_formatter
    raise FormatError('Unknown table format %s' % name)
FormatError: Unknown table format xls
>>>

(c) An example of using getattr()

In Exercise 5.2 you worked with a function print_portfolio() that made a table for a stock portfolio. That function was hard-coded to only work with stock data—how limiting! You can do so much more if you use functions such as getattr().

To begin, try this little example:

>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.1)
>>> columns = ['name', 'shares']
>>> for colname in columns:
        print colname, '=', getattr(s, colname)

name = GOOG
shares = 100
>>>

Carefully observe that the output data is determined entirely by the attribute names listed in the columns variable.

In the file tableformat.py, take this idea and expand it into a generalized function print_table() that simply prints a table showing user-specified attributes of a list of arbitrary objects. As with the earlier print_portfolio() function, print_table() should also accept a TableFormatter instance to control the output format. Here’s how it should work:

>>> import stock
>>> portfolio = stock.read_portfolio('Data/portfolio.csv')
>>> from tableformat import create_formatter, print_table
>>> formatter = create_formatter('txt')
>>> print_table(portfolio, ['name','shares'], formatter)
      name     shares
---------- ----------
        AA        100
       IBM         50
       CAT        150
      MSFT        200
        GE         95
      MSFT         50
       IBM        100

>>> print_table(portfolio, ['name','shares','price'], formatter)
      name     shares      price
---------- ---------- ----------
        AA        100       32.2
       IBM         50       91.1
       CAT        150      83.44
      MSFT        200      51.23
        GE         95      40.37
      MSFT         50       65.1
       IBM        100      70.44
>>>

(d) Bonus: Column Formatting

Modify the print_table() function in part (c) so that it also accepts a list of format strings for formatting the contents of each column. For example:

>>> print_table(portfolio,
                ['name','shares','price'],  ['%s','%d','$%0.2f'],
                formatter)
      name     shares      price
---------- ---------- ----------
        AA        100     $32.20
       IBM         50     $91.10
       CAT        150     $83.44
      MSFT        200     $51.23
        GE         95     $40.37
      MSFT         50     $65.10
       IBM        100     $70.44
>>>
Links