Modules and imports
Organize a small program into modules and classes while handling files, failures and iteration.
THE IDEA
A file of definitions you can reuse
A module is just a Python file. If prices.py defines total and TAX, another file can say import prices and use prices.total(...). Modules give related code a name and keep a big program from becoming one long file where every name collides with every other.
What import actually does the first time, in order: find the file (the folders in sys.path), create a module object, run the file top to bottom once to fill that object with names, store it in the cache sys.modules, and bind a name in your file. Every later import prices anywhere in the program skips straight to the cache.
Try it: import, from-import and the cache
Click statements in any order. Each one runs in the same fresh interpreter.
Module file prices.py
TAX = 0.2print("prices: loading")def total(items): return round(sum(items) * (1 + TAX), 2) Statements run so far
Import cache (sys.modules)
prices is not loaded
Your names
no names yet
Output
Honest note: this panel is a small simulation written for the lesson, checked against Python's real import system. The practice below uses the real import machinery on a file in a memory file system.
import pricesbinds one name,prices, to the module object. You reach things asprices.TAX.from prices import TAXbinds a new nameTAXto the value the module has right then. It is a copy of the reference, so laterprices.TAX = 0.3does not move yourTAX.- But the function
totalreadsTAXfrom inside the module, so it sees the new value. Same module, two different answers depending on which name you read.
Predict first. You run import prices, then from prices import total. How many times does prices: loading print?
Importing should not launch the program
Because importing runs top-level code, a module that starts a checkout at the bottom will start it for everyone who imports it, including tests. The convention is to put the work in a function and guard the call. When you run a file directly, Python sets its __name__ to "__main__". When it is imported, __name__ is the module name.
# prices.py
TAX = 0.2
def total(items):
return round(sum(items) * (1 + TAX), 2)
def main():
print("total:", total([10, 20]))
if __name__ == "__main__": # true for: python prices.py
main() # false for: import pricesChapter context
As a program grows, the difficult part becomes keeping state and responsibilities understandable. Use simple functions for transformations, classes for related state and behavior, and modules to give those responsibilities names.
TRY IT YOURSELF
Exercise 1: stop the module from running on import
shop_sourceis the text of a module calledshop. Right now it starts a checkout as soon as anyone imports it. Edit the text so importing it prints **nothing**.- Move the two
printlines into a functionmain(), and call it only underif __name__ == "__main__":. KeeptotalandTAXas they are. - The checks write your text to a real file in the in-memory file system, then import it with Python's real import system and also run it as a script.
Exercise 2: a copy or the live value?
make_modulebuilds a small module object calledsettingsand puts it insys.modules. This part is **simulated** so that you can see the import cache without needing a file. Real imports work the same way once the module is insys.modules.from settings import DEBUGcopies the value False into this file's own nameDEBUG. Later changes to the module do not reach that copy. Fixdebug_now()so it returns the **current** value of the module attributesettings.DEBUG.- Fix
is_cached(name)so it says whether a module with that name is already in the import cachesys.modules.
Both exercises must be solved to finish this lesson.
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
Should importing a price module launch the checkout?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
Reason about state ownership before adding abstraction. For a transformation f, repeated calls should have independent results unless shared state is explicitly intended. A lazy iterator retains only the state required for its next step; it does not imply that downstream consumers also use constant memory.
Worked example
An inventory starts with 5 items. reserve(2) leaves 3. A second independent inventory still contains 5. If both objects instead refer to the same list, mutating one changes what both observe. Draw object references as arrows to make this distinction explicit.
Reference code
The following example uses the Python standard library and does not call a remote service.
class Inventory:
def __init__(self, count):
self.count = count
def reserve(self, n):
if n > self.count:
raise ValueError("insufficient stock")
self.count -= n
first, second = Inventory(5), Inventory(5)
first.reserve(2)
assert (first.count, second.count) == (3, 5)
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
Type annotations are not validation. Catching Exception and continuing can hide corrupt state. Lazy iteration does not make a later list(...) memory-free.