Chapter 7. Modules in Python

A module is a file containing Python definitions and statements.

The Python Tutorial, https://docs.python.org/3/tutorial/modules.html

In this chapter, we’ll do a few things to improve the organization of our Python code. We’ll separate our test code from our production code using modules. We’ll see how the scoping and import rules in Python help us ensure the dependencies in our code are correct. Finally, we’ll remove a redundant test from our code, making things compact and meaningful.

Separating Our Code into Modules

We have production code for Money and Portfolio right next to our test code in the same file. We need to separate them into individual source files.

Let’s first create two new files named money.py and portfolio.py in the same folder as test_money.py. Our folder structure looks as shown below:

py
├── money.py
├── portfolio.py
└── test_money.py

We move the code for Money and Portfolio classes to money.py and portfolio.py, respectively. The code segment below shows the complete contents of portfolio.py after this code relocation:

import functools
import operator


class Portfolio:
    def __init__(self):
        self.moneys = []

    def add(self, *moneys):
        self.moneys.extend(moneys)

    def evaluate(self, currency):
        total = functools.reduce(operator.add,
                                 map(lambda m: m.amount, self.moneys), 0)
        return Money(total, currency)

Notice that we carry the two import statements along with the code for the Portfolio class, because Portfolio uses functools and operator.

The file money.py, not shown here, similarly contains the Money class and its methods.

When we run our tests now, we get our old friend, NameError arising from our tests:

  File "/Users/saleemsiddiqui/code/github/saleem/tdd-project/py/test_money.py",
          line 21, in testAddition
    fiveDollars = Money(5, "USD")
NameError: name 'Money' is not defined

We realize that the test class is dependent on both Money and Portfolio, so we add these import statements near at the top of test_money.py:

from money import Money
from portfolio import Portfolio

Ah: we now get NameError: name 'Money' is not defined from within Portfolio! A quick look at portfolio.py shows that it depends on Money, too. So we add from money import Money to the top of portfolio.py and all tests become green. Yay!

Moving code around and adding import statements makes the dependency tree of our code clearer. Figure 7-1 shows the dependency diagram of our code.

The dependency diagram of our Python code after separating it into three source files
Figure 7-1. The dependency diagram of our Python code after separating it into three source files

Removing redundancy in tests

We currently have two tests for multiplication, and one each for division and addition. The two tests for multiplication test the same functionality in the Money class. This is a bit of duplication we can do without. Let’s delete the testMultiplicationInDollars and rename the other test to simply testMultiplication. The resulting symmetry — three tests for the three features (Multiplication, Division, and Addition) where each test uses a different currency (Euros, Wons, and Dollars respectively) — is both compact and elegant.

Committing Our Changes

We have added a couple of new files and partitioned code among them. This is an especially opportune moment to commit our changes to our local Git repository.

git add .
git commit -m "refactor: moved Money and Portfolio classes their own Python files"

The output of these two commands should validate our changes:

[main c917e7c] refactor: moved Money and Portfolio classes their own Python files
 3 files changed, 30 insertions(+), 33 deletions(-)
 create mode 100644 py/money.py
 create mode 100644 py/portfolio.py

Where We Are

In this chapter, we separated Money and Portfolio into their own source files, which in Python, makes them their own modules. The separation ensured that the dependency from test code to production code was explicit and that there is no dependency in the other direction.

We also removed an extraneous test, thereby simplifying our code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.212.145