Your own file as a module
Any .py file is a module. `import pricing` finds pricing.py next to your program, runs its top-level code once, and shares that one module object with every file that imports it.
- Import a module of your own and use its functions and constants
- Explain why the module's top-level code runs only on the first import
- Use module-level state that every importer shares, and know when a function reads a module constant
Once two programs need the same function, copying it means fixing every bug twice. Put it in its own file instead: pricing.py is a module named pricing, and import pricing in any program in the same folder gives access to everything it defines.
The first import pricing finds the file (Python searches the folder of the program you ran first), runs its top-level code from top to bottom, and stores the finished module in a cache (sys.modules). Every later import, from any file, gets that same module object without running it again.
About this runner: in a real project you'd create pricing.py in your editor next to main.py. Here each run starts in an empty folder with one editor, so the examples create the module file from code first and call importlib.invalidate_caches(), which tells Python to look at the folder again because the file appeared after the program started. Everything from import pricing on is exactly what happens in a multi-file project.
Run each example to see what it prints. Running examples doesn't count toward progress.
from pathlib import Path import importlib Path("pricing.py").write_text('''TAX_RATE = 0.07 print("pricing module loaded") def with_tax(amount): return amount * (1 + TAX_RATE) ''', encoding="utf-8") importlib.invalidate_caches() import pricing import pricing print(f"{pricing.with_tax(66.49):.2f}") print(pricing.TAX_RATE, pricing.__name__)
from pathlib import Path import importlib Path("pricing.py").write_text("TAX_RATE = 0.07\n", encoding="utf-8") importlib.invalidate_caches() import pricing print(pricing.with_tax(10))
Because a module runs once and is shared, a variable at module level is shared too. Here inventory holds the stock, and checkout (another module) imports it and takes stock away. The main program sees the change, because there is only one inventory.
from pathlib import Path import importlib Path("inventory.py").write_text('''STOCK = {"HOM-52782": 4, "GRO-63674": 12} def take(sku, quantity): if STOCK.get(sku, 0) < quantity: return False STOCK[sku] -= quantity return True ''', encoding="utf-8") Path("checkout.py").write_text('''import inventory def buy(sku, quantity): return "paid" if inventory.take(sku, quantity) else "out of stock" ''', encoding="utf-8") importlib.invalidate_caches() import checkout import inventory print(checkout.buy("HOM-52782", 3), checkout.buy("HOM-52782", 3)) print(inventory.STOCK) print(checkout.inventory is inventory)
When to use it: as soon as code is reused by more than one program, or a file grows too long to read. Give modules plain lower-case names. Mistakes to avoid: naming your file after a standard module (json.py, csv.py), doing slow work or printing at module level (it happens on import), and expecting edits to a module to show up in a program that already imported it (it was cached).
- A .py file is a module;
import nameruns it once and caches it, and every importer shares that one object. - Module-level constants and dictionaries are shared state; functions read module names when they run.
- Keep modules to definitions: top-level work runs on import.
- Next: a folder of modules, a package.