Redefining Special Methods

PythonPythonBeginner
Practice Now

This tutorial is from open-source community. Access the source code

Introduction

Objectives:

  • Learn how to customize the behavior of objects by redefining special methods.
  • Change the way that user-defined objects get printed
  • Make objects comparable
  • Create a context manager

Files Modified: stock.py


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("`Python`")) -.-> python/BasicConceptsGroup(["`Basic Concepts`"]) python(("`Python`")) -.-> python/FileHandlingGroup(["`File Handling`"]) python(("`Python`")) -.-> python/ControlFlowGroup(["`Control Flow`"]) python(("`Python`")) -.-> python/DataStructuresGroup(["`Data Structures`"]) python(("`Python`")) -.-> python/FunctionsGroup(["`Functions`"]) python(("`Python`")) -.-> python/ModulesandPackagesGroup(["`Modules and Packages`"]) python(("`Python`")) -.-> python/ObjectOrientedProgrammingGroup(["`Object-Oriented Programming`"]) python(("`Python`")) -.-> python/ErrorandExceptionHandlingGroup(["`Error and Exception Handling`"]) python(("`Python`")) -.-> python/AdvancedTopicsGroup(["`Advanced Topics`"]) python(("`Python`")) -.-> python/PythonStandardLibraryGroup(["`Python Standard Library`"]) python/BasicConceptsGroup -.-> python/comments("`Comments`") python/FileHandlingGroup -.-> python/with_statement("`Using with Statement`") python/BasicConceptsGroup -.-> python/variables_data_types("`Variables and Data Types`") python/BasicConceptsGroup -.-> python/numeric_types("`Numeric Types`") python/BasicConceptsGroup -.-> python/strings("`Strings`") python/BasicConceptsGroup -.-> python/booleans("`Booleans`") python/ControlFlowGroup -.-> python/conditional_statements("`Conditional Statements`") python/ControlFlowGroup -.-> python/for_loops("`For Loops`") python/ControlFlowGroup -.-> python/list_comprehensions("`List Comprehensions`") python/DataStructuresGroup -.-> python/lists("`Lists`") python/DataStructuresGroup -.-> python/tuples("`Tuples`") python/DataStructuresGroup -.-> python/sets("`Sets`") python/FunctionsGroup -.-> python/function_definition("`Function Definition`") python/ModulesandPackagesGroup -.-> python/importing_modules("`Importing Modules`") python/ModulesandPackagesGroup -.-> python/using_packages("`Using Packages`") python/ModulesandPackagesGroup -.-> python/standard_libraries("`Common Standard Libraries`") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("`Classes and Objects`") python/ObjectOrientedProgrammingGroup -.-> python/constructor("`Constructor`") python/ObjectOrientedProgrammingGroup -.-> python/polymorphism("`Polymorphism`") python/ObjectOrientedProgrammingGroup -.-> python/encapsulation("`Encapsulation`") python/ErrorandExceptionHandlingGroup -.-> python/raising_exceptions("`Raising Exceptions`") python/FileHandlingGroup -.-> python/file_opening_closing("`Opening and Closing Files`") python/FileHandlingGroup -.-> python/file_operations("`File Operations`") python/AdvancedTopicsGroup -.-> python/context_managers("`Context Managers`") python/PythonStandardLibraryGroup -.-> python/date_time("`Date and Time`") python/PythonStandardLibraryGroup -.-> python/os_system("`Operating System and System`") python/BasicConceptsGroup -.-> python/python_shell("`Python Shell`") python/FunctionsGroup -.-> python/build_in_functions("`Build-in Functions`") subgraph Lab Skills python/comments -.-> lab-132496{{"`Redefining Special Methods`"}} python/with_statement -.-> lab-132496{{"`Redefining Special Methods`"}} python/variables_data_types -.-> lab-132496{{"`Redefining Special Methods`"}} python/numeric_types -.-> lab-132496{{"`Redefining Special Methods`"}} python/strings -.-> lab-132496{{"`Redefining Special Methods`"}} python/booleans -.-> lab-132496{{"`Redefining Special Methods`"}} python/conditional_statements -.-> lab-132496{{"`Redefining Special Methods`"}} python/for_loops -.-> lab-132496{{"`Redefining Special Methods`"}} python/list_comprehensions -.-> lab-132496{{"`Redefining Special Methods`"}} python/lists -.-> lab-132496{{"`Redefining Special Methods`"}} python/tuples -.-> lab-132496{{"`Redefining Special Methods`"}} python/sets -.-> lab-132496{{"`Redefining Special Methods`"}} python/function_definition -.-> lab-132496{{"`Redefining Special Methods`"}} python/importing_modules -.-> lab-132496{{"`Redefining Special Methods`"}} python/using_packages -.-> lab-132496{{"`Redefining Special Methods`"}} python/standard_libraries -.-> lab-132496{{"`Redefining Special Methods`"}} python/classes_objects -.-> lab-132496{{"`Redefining Special Methods`"}} python/constructor -.-> lab-132496{{"`Redefining Special Methods`"}} python/polymorphism -.-> lab-132496{{"`Redefining Special Methods`"}} python/encapsulation -.-> lab-132496{{"`Redefining Special Methods`"}} python/raising_exceptions -.-> lab-132496{{"`Redefining Special Methods`"}} python/file_opening_closing -.-> lab-132496{{"`Redefining Special Methods`"}} python/file_operations -.-> lab-132496{{"`Redefining Special Methods`"}} python/context_managers -.-> lab-132496{{"`Redefining Special Methods`"}} python/date_time -.-> lab-132496{{"`Redefining Special Methods`"}} python/os_system -.-> lab-132496{{"`Redefining Special Methods`"}} python/python_shell -.-> lab-132496{{"`Redefining Special Methods`"}} python/build_in_functions -.-> lab-132496{{"`Redefining Special Methods`"}} end

Better output for representing 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. Here is an example that illustrates using dates:

>>> from datetime import date
>>> d = date(2008, 7, 5)
>>> print(d)              ## uses str()
2008-07-05
>>> d    ## uses repr()
datetime.date(2008, 7, 5)
>>>

There are several techniques for obtaining the repr() string in output:

>>> print('The date is', repr(d))
The date is datetime.date(2008, 7, 5)
>>> print(f'The date is {d!r}')
The date is datetime.date(2008, 7, 5)
>>> print('The date is %r' % d)
The date is datetime.date(2008, 7, 5)
>>>

Modify the Stock object that you've created so that the __repr__() method produces more useful output. For example:

>>> goog = Stock('GOOG', 100, 490.10)
>>> 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, reader
>>> portfolio = reader.read_csv_as_instances('portfolio.csv', stock.Stock)
>>> portfolio
[Stock('AA', 100, 32.2), Stock('IBM', 50, 91.1), Stock('CAT', 150, 83.44), Stock('MSFT', 200, 51.23),
 Stock('GE', 95, 40.37), Stock('MSFT', 50, 65.1), Stock('IBM', 100, 70.44)]
>>>

Making objects comparable

What happens if you create two identical Stock objects and try to compare them? Find out:

>>> a = Stock('GOOG', 100, 490.1)
>>> b = Stock('GOOG', 100, 490.1)
>>> a == b
False
>>>

You can fix this by giving the Stock class an __eq__() method. For example:

class Stock:
    ...
    def __eq__(self, other):
        return isinstance(other, Stock) and ((self.name, self.shares, self.price) ==
                                             (other.name, other.shares, other.price))
    ...

Make this change and try comparing two objects again.

A Context Manager

In Exercise 3.5, you made it possible for users to make nicely formatted tables. For example:

>>> from tableformat import create_formatter
>>> formatter = create_formatter('text')
>>> 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
>>>

One issue with the code is that all tables are printed to standard out (sys.stdout). Suppose you wanted to redirect the output to a file or some other location. In the big picture, you might modify all of the table formatting code to allow a different output file. However, in a pinch, you could also solve this with a context manager.

Define the following context manager:

>>> import sys
>>> class redirect_stdout:
        def __init__(self, out_file):
            self.out_file = out_file
        def __enter__(self):
            self.stdout = sys.stdout
            sys.stdout = self.out_file
            return self.out_file
        def __exit__(self, ty, val, tb):
            sys.stdout = self.stdout

This context manager works by making a temporary patch to sys.stdout to cause all output to redirect to a different file. On exit, the patch is reverted. Try it out:

>>> from tableformat import create_formatter
>>> formatter = create_formatter('text')
>>> with redirect_stdout(open('out.txt', 'w')) as file:
        tableformat.print_table(portfolio, ['name','shares','price'], formatter)
        file.close()

>>> ## Inspect the file
>>> print(open('out.txt').read())
      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
>>>

Summary

Congratulations! You have completed the Redefining Special Methods lab. You can practice more labs in LabEx to improve your skills.

Other Python Tutorials you may like