What you'll learn
Real programs don't live in one giant file. Modules (individual .py files) and packages (folders of modules) let you split code into logical pieces and reuse it. You've already imported from the standard library — now you'll organize your own code the same way.
By the end you'll be able to:
- Import in every style:
import,from ... import, andas - Split code into your own modules
- Group modules into a package
- Use the
if __name__ == "__main__"idiom
Importing modules
A module is just a .py file. import brings its names into your program. There are three common forms — import the whole module (and use dotted access), import specific names, or import under a shorter alias:
import math # whole module
from random import randint # one name from a module
import datetime as dt # with an alias
print(math.sqrt(16)) # 4.0
print(randint(1, 6)) # e.g. 4
print(dt.date.today().year) # e.g. 2026Tip
import module or from module import specific_name over from module import *. The star import dumps every name into your namespace, hiding where things came from and risking silent name clashes. Explicit is better than implicit.Your own modules
Importing your own code works identically — the file name (without .py) is the module name. Put reusable functions and constants in one file and import them from another:
# --- greetings.py ---
def hello(name):
return f"Hello, {name}!"
PI = 3.14159
# --- main.py ---
import greetings
from greetings import PI
print(greetings.hello("Ada"))
print(PI)When you import a module, Python runs it once and caches it, so top-level code executes a single time no matter how many files import it.
Packages
A package is a folder of modules. Historically it needed an __init__.py file (which can be empty, or expose the package's public API); modern Python also supports packages without it. You import with dotted paths that mirror the folders:
myapp/
├── __init__.py
├── models/
│ ├── __init__.py
│ └── user.py # defines class User
└── utils/
├── __init__.py
└── text.py # defines slugify()
# elsewhere:
from myapp.models.user import User
from myapp.utils.text import slugifyNote
sys.path): roughly the current directory, then installed packages, then the standard library. If an import fails with ModuleNotFoundError, it's almost always because the module isn't on that path — often a working-directory or virtual-environment issue (next module).The __name__ == "__main__" idiom
Every module has a built-in __name__ variable. When you run a file directly, __name__ is "__main__"; when it's imported, __name__ is the module's name. This lets a file provide reusable functions and a script entry point without the script code running on import:
# calc.py
def add(a, b):
return a + b
def main():
print(add(2, 3))
if __name__ == "__main__": # only runs when executed directly,
main() # NOT when imported by another fileKey idea
if __name__ == "__main__":. Then importing the module gives you its functions without side effects, and running it directly executes the script. It's the standard shape of almost every runnable Python file.Recap & quick check
Key takeaways
- A module is a .py file; import its names with import, from ... import, or an as alias.
- Avoid from module import * — it pollutes your namespace and hides origins.
- A package is a folder of modules, imported with dotted paths (myapp.utils.text).
- Python runs a module once on first import and caches it; it finds modules via sys.path.
- Guard script code with if __name__ == '__main__' so it runs only when executed directly.
Quick check
1. How do you import just randint from the random module?
2. Why avoid 'from module import *'?
3. What is a package?
4. When you run a file directly, __name__ equals…
5. Why guard code with if __name__ == '__main__'?
Your code is organized. But how do you manage the external packages a project needs? Next up: Module 19 — Virtual Environments, pip & the Ecosystem.