description: "Modules, Packages & pip — init.py is simply the code that runs when a package is first imported. pip install requests downloads a wheel and unpacks it…"---
09 · Modules, Packages & pip¶
Importing from the standard library¶
import math
from datetime import date
from collections import Counter
print(math.sqrt(16)) # 4.0
print(date.today())
print(Counter(["a", "b", "a"])) # Counter({'a': 2, 'b': 1})
Writing your own module¶
shapes.py:
# shapes.py
def area_circle(radius):
return 3.14159 * radius ** 2
def area_square(side):
return side ** 2
main.py, in the same folder:
import shapes
print(shapes.area_circle(2))
print(shapes.area_square(3))
# or import specific names:
from shapes import area_circle
print(area_circle(2))
Packages (folders of modules)¶
__init__.py marks the folder as a package (can be empty). Then:
Virtual environments¶
Every project should have its own isolated environment so dependencies don't clash between projects:
python3 -m venv .venv
source .venv/bin/activate # macOS/Linux
# .venv\Scripts\activate # Windows
pip install requests
pip freeze > requirements.txt # save exact dependency versions
pip install -r requirements.txt # recreate the environment elsewhere
deactivate
Installing third-party packages with pip¶
How It Actually Works¶
import shapes runs the import system, and the single most important fact
is that a module's code runs once per process:
- Cache check. Python looks up
"shapes"insys.modules, a dict of every module already imported. A hit returns immediately — this is why circular imports don't loop forever and why a module is a natural singleton. - Finding. On a miss, Python asks each finder in
sys.meta_path. The path-based finder walkssys.path(current dir / script dir, thenPYTHONPATH, then the standard library, thensite-packages) looking forshapes.py, ashapes/package with__init__.py, a C extension, etc. - Loading. The matching loader reads the source, checks for a valid
cached
__pycache__/shapes.cpython-XY.pyc(compares source mtime/hash and Python version), compiles it if stale, and writes the.pycback. - Execution. Python creates an empty module object, inserts it into
sys.modulesfirst (so partial circular imports can see it), then executes the module body top to bottom in that module's namespace.defs and assignments populate the module's__dict__. - Binding. Finally the name
shapesis bound in your namespace to that module object.from shapes import area_circledoes the same load, then copies just that one attribute into your namespace.
__init__.py is simply the code that runs when a package is first imported.
pip install requests downloads a wheel and unpacks it into site-packages/,
which is already on sys.path — so import requests then just works via the
same five steps.
🔀 See this in another language¶
Exercise¶
Split a script that manages a to-do list into two modules: storage.py
(load/save the list to a JSON file) and main.py (the CLI logic that imports
storage). This sets up the project for Module 10.