From scripts to real projects
The habits in this topic are what separate a working script from code a team can trust: each project in its own environment, a clear folder layout, consistent style, checked types, proper logs, automatic tests and version control.
venv: a separate box of packages for each project
Different projects need different packages, sometimes different versions of the same one. A virtual environment gives each project its own private set of packages.
python -m venv .venv # create it, once per project
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS and Linux
pip install requests pandas # installs into this project only
pip freeze > requirements.txt # record the exact versions
pip install -r requirements.txt # recreate them on another computer
deactivate
Do not copy the .venv folder or add it to Git; share requirements.txt instead.
Project structure and PEP 8 style
school_app/
.venv/
school_app/
__init__.py
marks.py
reports.py
tests/
test_marks.py
requirements.txt
README.md
main.py
PEP 8 is Python's official style guide. The key rules:
| Rule | Example |
|---|---|
| 4 spaces per indent level | never tabs |
snake_case for variables and functions | total_marks, get_grade() |
CapWords for classes | StudentRecord |
UPPER_CASE for constants | PASS_MARK = 33 |
| spaces around operators | x = a + b, not x=a+b |
| short lines | about 79 to 99 characters |
| imports at the top, one per line | import os then import sys |
Tools such as black or ruff can format and check your code automatically.
Type hints: say what types a function expects
def average(marks: list[float]) -> float:
return sum(marks) / len(marks)
name: str = "Asha"
age: int | None = None # int or None
Type hints do not change how the code runs; Python ignores them at run time. They help your editor warn you, document the code, and let checkers find bugs.
Catch bugs before running: mypy
pip install mypy
mypy school_app
def grade(marks: int) -> str:
return "Pass" if marks >= 33 else "Fail"
grade("90") # mypy: Argument 1 has incompatible type "str"; expected "int"
logging: better than print for real apps
import logging
logging.basicConfig(
filename="bank.log",
level=logging.INFO,
format="%(asctime)s %(levelname)s %(message)s",
)
log = logging.getLogger(__name__)
log.info("Deposit of Rs %s to account %s", 500, "A101")
log.warning("Balance below minimum")
log.error("Withdrawal failed: insufficient balance")
| Level | Use it for |
|---|---|
DEBUG | detail while you are developing |
INFO | normal events: a user logged in |
WARNING | something odd, but the program carries on |
ERROR | an operation failed |
CRITICAL | the program cannot continue |
Unlike print, logs carry the time and level, can go to a file, and can be turned up or down without deleting lines of code.
Automated tests with pytest
A test is code that checks your code. Write tests once, then run them after every change to catch anything you broke.
# calculator.py
def divide(a, b):
if b == 0:
raise ValueError("cannot divide by zero")
return a / b
# tests/test_calculator.py
import pytest
from calculator import divide
def test_divide():
assert divide(10, 2) == 5
def test_divide_by_zero():
with pytest.raises(ValueError):
divide(1, 0)
pip install pytest
pytest # finds files named test_*.py and runs every test_ function
Git: save versions and share your code
git init # start tracking a project
git add . # stage changes
git commit -m "Add marks report" # save a version with a message
git log --oneline # see the history
git push # upload to GitHub
Add a .gitignore file listing .venv/, __pycache__/ and secret files such as .env, so they are never committed. Our separate Git & GitHub course covers this in depth.
Common mistakes
- Installing every package globally, until two projects need different versions.
- Committing passwords or API keys to Git.
- Leaving debugging
print()calls in finished code instead of usinglogging. - Writing tests that only check the happy path and never the errors.
Key points
- One
venvper project; record packages inrequirements.txt. - Follow PEP 8 so code looks the same across a team.
- Type hints and mypy catch type mistakes before the code runs.
- Use
loggingfor real apps,pytestfor automatic checks and Git for history.
Practice questions
1. Why use a virtual environment?
To keep each project's packages and versions separate, so installing something for one project cannot break another.
2. Rename these to follow PEP 8: TotalMarks (a variable), calcgrade (a function), student (a class).
total_marks, calc_grade and Student.
3. Do type hints stop average("abc") from running?
No. Python ignores hints at run time; a checker such as mypy or your editor reports the mistake.
4. Write a pytest test that checks add(2, 3) returns 5.
def test_add():
assert add(2, 3) == 55. Which logging level would you use for "user entered a wrong password"?
WARNING. It is unusual and worth recording, but the program carries on.