A single file is fine up to a few hundred lines. Past that, finding anything becomes the hard part. Splitting a program into modules is mostly about making it navigable.
Every playground on this page holds several files — use the tabs above the editor to move between them. Run always executes the entry file, marked with a dot.
Two files
from tax import with_tax looks for tax.py in the same folder. Everything
defined at the top level of that file — functions, classes, constants — is
importable.
Packages
A folder with an __init__.py is a package: a module that contains other
modules. It lets you group related files and gives the group a name.
Note from .cart import Cart inside __init__.py — the leading dot means
“from this package”, a relative import. Use relative imports between modules
of the same package, and absolute imports (from shop import Cart) from
outside it.
The main guard
Code at the top level of a module runs when it is imported. That is fine for definitions, and a problem for anything that acts.
Run it: the unguarded print fires during the import, while the guarded block
does not. __name__ is "__main__" in the file you executed and the module’s
own name everywhere else. Put your script’s actual work behind that guard, so
the file can be both imported and run.
Circular imports
If a.py imports b.py and b.py imports a.py, Python cannot finish either.
The fix is almost never a clever import trick — it is to notice that the two modules are really one concern split badly, or that a third module should hold what they share.
A layout that scales
myproject/
├── README.md
├── requirements.txt
├── src/
│ └── shop/
│ ├── __init__.py # what the package exposes
│ ├── cart.py # one concern per module
│ ├── pricing.py
│ └── cli.py # the entry point, behind a main guard
└── tests/
├── test_cart.py
└── test_pricing.py
Two rules carry most of the weight: one concern per module, and
dependencies point one way — cli imports cart, cart never imports
cli.
Split a program up
The single file below does three things at once. Split it into temperature.py
(the conversions), report.py (the formatting) and main.py (the entry point,
behind a main guard). Use the file tabs to add code to each.
Show one solution
temperature.py holds the pure calculations, report.py turns numbers into
text, and main.py decides what to do — each importable without side effects:
# temperature.py
def to_fahrenheit(celsius):
return celsius * 9 / 5 + 32
def to_kelvin(celsius):
return celsius + 273.15# report.py
from temperature import to_fahrenheit, to_kelvin
def format_row(celsius):
return f"{celsius:6.1f}C {to_fahrenheit(celsius):6.1f}F {to_kelvin(celsius):6.1f}K"
def format_table(values):
return "\n".join(format_row(c) for c in values)# main.py
from report import format_table
def main():
print(format_table([-40, 0, 21.5, 100]))
if __name__ == "__main__":
main()Dependencies point one way — main → report → temperature — so
temperature.py can be tested without anything else existing.
What you learned
- Any
.pyfile beside yours is importable as a module. - A folder with
__init__.pyis a package;from .module import Xis a relative import. if __name__ == "__main__":separates “imported” from “run directly”.- Circular imports mean the split is wrong, not that you need a trick.
- One concern per module, and dependencies pointing one way.