Virtual Environments, Dependencies, and Modules
The moment your Python work involves installing libraries like pandas or an AI SDK, you need to manage environments and dependencies properly. Skip this and you get the classic "it works on my machine" nightmare. This lesson covers virtual environments, pinning dependencies, and splitting your code into reusable modules, the practical plumbing of every real project.
What You'll Learn
- Why each project deserves its own virtual environment
- How to create one and install dependencies into it
- How to record dependencies so others can reproduce your setup
- How to organize code into modules and packages you can import
Why Virtual Environments
If you install every library globally, two projects that need different versions of the same package will collide. A virtual environment is an isolated folder of packages for one project, so projects never step on each other.
Python ships with venv built in, so it is always available:
# create an environment in a folder called .venv
python -m venv .venv
# activate it (macOS/Linux)
source .venv/bin/activate
# activate it (Windows)
.venv\Scripts\activate
Once activated, pip install puts packages inside that environment only. Deactivate with deactivate. A newer, much faster all-in-one tool called uv has become popular for the same job and can create environments and install packages in a fraction of the time; venv plus pip remains the universally available baseline and is all you need to learn the concepts.
Installing and Recording Dependencies
Install what your project needs while the environment is active:
pip install pandas requests
Then freeze the exact versions so anyone can reproduce your setup:
pip freeze > requirements.txt
Now a teammate (or future you) recreates the environment with:
pip install -r requirements.txt
Pinning versions is what prevents "it worked last month" surprises. Commit requirements.txt to your project; never commit the .venv folder itself.
- python -m venv .venvcreate
- activateisolate
- pip install ...add libraries
- pip freeze > requirements.txtrecord
Modules: Splitting Code Into Files
A module is just a .py file. Anything you define in one file can be imported into another. Once a script grows past a screen or two, split it.
# cleaning.py
def strip_names(rows):
return [r.strip().title() for r in rows]
# main.py
from cleaning import strip_names
print(strip_names([" alice ", "BOB"]))
Import only what you need (from cleaning import strip_names) rather than everything, so it is clear where each name comes from.
Packages: Folders of Modules
A package is a folder containing modules. Group related files together:
myproject/
data/
cleaning.py
loading.py
main.py
# main.py
from data.cleaning import strip_names
from data.loading import load_csv
In modern Python a plain folder works as a package for imports. Organizing code this way keeps a growing project navigable instead of one giant file.
The if __name__ == "__main__" Guard
Put runnable code behind this guard so a file can be both imported as a module and run directly, without the run code firing on import:
def main():
print("running the pipeline")
if __name__ == "__main__":
main() # only runs when you execute this file directly
This is a near-universal convention you will see in almost every Python project.
Key Takeaways
- Give each project its own virtual environment so dependencies never collide.
python -m venv .venvthen activate it;pip installthen goes into that environment only.- Record exact versions with
pip freeze > requirements.txtand restore withpip install -r requirements.txt. - A module is a
.pyfile; a package is a folder of modules. Split code as it grows and import only what you need. - Guard runnable code with
if __name__ == "__main__":so files can be imported and run.

