Introduction
Objectives:
- Learn about controlling symbols and combining submodules
- Learn about module splitting
This tutorial is from open-source community. Access the source code
Objectives:
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:
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 export Structure
reader.py
should export all of the various read_csv_as_*()
functionstableformat.py
exports create_formatter()
and print_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):
name = String()
shares = PositiveInteger()
price = PositiveFloat()
@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
portfolio = read_csv_as_instances('portfolio.csv', Stock)
formatter = create_formatter('text')
print_table(portfolio, ['name','shares','price'], formatter)
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):
name = String()
shares = PositiveInteger()
price = PositiveFloat()
@property
def cost(self):
return self.shares * self.price
def sell(self, nshares: PositiveInteger):
self.shares -= nshares
if __name__ == '__main__':
portfolio = read_csv_as_instances('portfolio.csv', Stock)
formatter = create_formatter('text')
print_table(portfolio, ['name','shares','price'], formatter)
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.
The file structly/tableformat.py
contains code for creating tables in different formats. Specifically:
TableFormatter
base class.TextTableFormatter
class.CSVTableFormatter
class.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
).
mkdir tableformat
Move the original tableformat.py
file into the new tableformat
directory and rename it to formatter.py
.
mv tableformat.py tableformat/formatter.py
In the tableformat
directory, split the tableformat.py
code into the following files and directories:
formatter.py
- Contains the TableFormatter
base class, mixins, and various functions.formats/text.py
- Contains the TextTableFormatter
class.formats/csv.py
- Contains the CSVTableFormatter
class.formats/html.py
- Contains the HTMLTableFormatter
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):
name = String()
shares = PositiveInteger()
price = PositiveFloat()
@property
def cost(self):
return self.shares * self.price
def sell(self, nshares):
self.shares -= nshares
if __name__ == '__main__':
portfolio = read_csv_as_instances('portfolio.csv', Stock)
formatter = create_formatter('text')
print_table(portfolio, ['name','shares','price'], formatter)
Congratulations! You have completed the Controlling Symbols and Combining Submodules lab. You can practice more labs in LabEx to improve your skills.