5.9 KiB
[ Index | Exercise 9.2 | Exercise 9.4 ]
Exercise 9.3
Objectives:
- Learn about controlling symbols and combining submodules
- Learn about module splitting
One potentially annoying aspect of packages is that they complicate
import statements. For example, in the stock.py
program,
you now have import statements such as the following:
from structly.structure import Structure
from structly.reader import read_csv_as_instances
from structly.tableformat import create_formatter, print_table
If the package is meant to be used as a unified whole, it might be more sane (and easier) to consolidate everything into a single top level package. Let’s do that:
(a) Controlling Exported Symbols
Modify all of the submodules in the structly
package so
that they explicitly define an __all__
variable which
exports selected symbols. Specifically:
structure.py
should exportStructure
reader.py
should export all of the variousread_csv_as_*()
functionstableformat.py
exportscreate_formatter()
andprint_table()
Now, in the __init__.py
file, unify all of the
submodules like this:
# structly/__init__.py
from .structure import *
from .reader import *
from .tableformat import *
Once you have done this, you should be able to import everything from a single logical module:
# stock.py
from structly import Structure
class Stock(Structure):
= String()
name = PositiveInteger()
shares = PositiveFloat()
price
@property
def cost(self):
return self.shares * self.price
def sell(self, nshares: PositiveInteger):
self.shares -= nshares
if __name__ == '__main__':
from structly import read_csv_as_instances, create_formatter, print_table
= read_csv_as_instances('Data/portfolio.csv', Stock)
portfolio = create_formatter('text')
formatter 'name','shares','price'], formatter) print_table(portfolio, [
(b) Exporting Everything
In the structly/__init__.py
, define an
__all__
variable that contains all exported symbols. Once
you’ve done this, you should be able to simplify the
stock.py
file further:
# stock.py
from structly import *
class Stock(Structure):
= String()
name = PositiveInteger()
shares = PositiveFloat()
price
@property
def cost(self):
return self.shares * self.price
def sell(self, nshares: PositiveInteger):
self.shares -= nshares
if __name__ == '__main__':
= read_csv_as_instances('Data/portfolio.csv', Stock)
portfolio = create_formatter('text')
formatter 'name','shares','price'], formatter) print_table(portfolio, [
As an aside, use of the from module import *
statement
is generally frowned upon the Python community–especially if you’re not
sure what you’re doing. That said, there are situations where it often
makes sense. For example, if a package defines a large number of
commonly used symbols or constants it might be useful to use it.
(c) Module Splitting
The file structly/tableformat.py
contains code for
creating tables in different formats. Specifically:
- A
TableFormatter
base class. - A
TextTableFormatter
class. - A
CSVTableFormatter
class. - A
HTMLTableFormatter
class.
Instead of having all of these classes in a single .py
file, maybe it would make sense to move each concrete formatter to its
own file. To do this, we’re going to split the
tableformat.py
file into parts. Follow these instructions
carefully:
First, remove the structly/__pycache__
directory.
% cd structly
% rm -rf __pycache__
Next, create the directory structly/tableformat
. This
directory must have exactly the same name as the module it is replacing
(tableformat.py
).
bash % mkdir tableformat
bash %
Move the original tableformat.py
file into the new
tableformat
directory and rename it to
formatter.py
.
bash % mv tableformat.py tableformat/formatter.py
bash %
In the tableformat
directory, split the
tableformat.py
code into the following files and
directories:
formatter.py
- Contains theTableFormatter
base class, mixins, and various functions.formats/text.py
- Contains theTextTableFormatter
class.formats/csv.py
- Contains theCSVTableFormatter
class.formats/html.py
- Contains theHTMLTableFormatter
class.
Add an __init__.py
file to the tableformat/
and tableformat/formats
directories. Have the
tableformat/__init__.py
export the same symbols that the
original tableformat.py
file exported.
After you have made all of these changes, you should have a package structure that looks like this:
structly/
__init__.py
validate.py
reader.py
structure.py
tableformat/
__init__.py
formatter.py
formats/
__init__.py
text.py
csv.py
html.py
To users, everything should work exactly as it did before. For
example, your prior stock.py
file should work:
# stock.py
from structly import *
class Stock(Structure):
= String()
name = PositiveInteger()
shares = PositiveFloat()
price
@property
def cost(self):
return self.shares * self.price
def sell(self, nshares):
self.shares -= nshares
if __name__ == '__main__':
= read_csv_as_instances('Data/portfolio.csv', Stock)
portfolio = create_formatter('text')
formatter 'name','shares','price'], formatter) print_table(portfolio, [
[ Solution | Index | Exercise 9.2 | Exercise 9.4 ]
>>>
Advanced Python Mastery
...
A course by dabeaz
...
Copyright 2007-2023
.
This work is licensed under a Creative Commons
Attribution-ShareAlike 4.0 International License