Learning goals
Write maintainable functions, avoid shared defaults, organize projects, and verify important behavior.
Explanation
Follow PEP 8 conventions: four-space indentation, snake_case for functions and variables, CapWords for classes, and UPPER_CASE for constants. PEP 8 recommends a 79-character limit for code lines, while some teams deliberately choose a different formatter limit. Consistency and readability matter more than blindly mixing conventions.
Keep a small project's structure simple: application modules, tests, README.md, requirements.txt, and .gitignore. Larger packaged projects can use a src directory with installation configuration. Keep .venv, caches, and generated practice output out of version control. Explain how to install and run the project in its README.
Example 1 Avoid a shared mutable default
def add_tag(tag, tags=None):
"""Return a list containing the supplied tag."""
if tags is None:
tags = []
tags.append(tag)
return tags
first = add_tag("python")
second = add_tag("testing")
print(first)
print(second)Expected output: ['python'] followed by ['testing']. A default tags=[] would be created once at function definition time and reused. The None pattern creates a fresh list for each omitted argument. When an existing list is passed, this function intentionally mutates it; document that behavior or copy it if mutation is unwanted.
Example 2 Small functions and meaningful checks
def average_score(scores):
"""Return the mean of a nonempty sequence of scores."""
if not scores:
raise ValueError("At least one score is required")
return sum(scores) / len(scores)
assert average_score([60, 75, 90]) == 75
assert average_score([100]) == 100
try:
average_score([])
except ValueError:
print("Empty input correctly rejected")
else:
raise AssertionError("Expected ValueError")Expected output: Empty input correctly rejected. These checks cover a normal case, a single item, and an invalid boundary. Assertions are useful here for tests; do not rely on them for essential production validation because optimized execution can disable them.
Dependencies and logging
After installing only the project's dependencies into a clean environment, record them:
python -m pip freeze > requirements.txt
python -m pip install -r requirements.txtThe first command overwrites requirements.txt with installed versions. A clean environment makes that list more relevant. For scripts that will run unattended, use logging instead of scattered print calls:
import logging
logging.basicConfig(level=logging.INFO,
format="%(levelname)s %(message)s")
logging.info("Processed %s records", 3)Expected message: INFO Processed 3 records. Avoid logging credentials or sensitive record contents. Write comments explaining intent; clear function names should explain ordinary mechanics.
Student tasks
1. Refactor a previous lesson's script into at least two focused functions.
2. Add tests for a normal case, an empty input, and a boundary value.
3. Write a README with the Python requirement, setup commands, run command, and sample output.
4. Explain and demonstrate the mutable-default bug, then fix it using None.
Completion check: another student should be able to run your project from the README alone.