Skip to main content

Module 34 - Best Practices

Learn industry best practices for writing clean, maintainable, and professional Python code.


1. PEP 8 Style Guide

Naming Conventions

# ✅ Good
class UserAccount:
pass

def calculate_total_price():
pass

MAX_CONNECTIONS = 100
user_name = "Alice"

# ❌ Bad
class user_account:
pass

def CalculateTotalPrice():
pass

max_connections = 100
UserName = "Alice"

Code Layout

# ✅ Good: 4 spaces indentation
def function():
if condition:
do_something()
else:
do_something_else()

# ✅ Good: Blank lines
class MyClass:
"""Class docstring."""

def __init__(self):
pass

def method(self):
pass

# ✅ Good: Line length <= 79 characters
result = some_function(
argument1,
argument2,
argument3
)

2. Documentation

Docstrings

def calculate_discount(price, discount_percent):
"""
Calculate discounted price.

Args:
price (float): Original price
discount_percent (float): Discount percentage (0-100)

Returns:
float: Discounted price

Examples:
>>> calculate_discount(100, 20)
80.0
"""
return price * (1 - discount_percent / 100)

class User:
"""
Represents a user account.

Attributes:
username (str): User's username
email (str): User's email address
"""

def __init__(self, username, email):
self.username = username
self.email = email

3. Error Handling

# ✅ Good: Specific exceptions
try:
result = 10 / 0
except ZeroDivisionError as e:
print(f"Error: {e}")

# ✅ Good: Finally clause
try:
file = open('data.txt')
process(file)
finally:
file.close()

# ✅ Better: Context manager
with open('data.txt') as file:
process(file)

# ❌ Bad: Bare except
try:
risky_operation()
except:
pass

4. Type Hints

from typing import List, Dict, Optional

def process_users(
users: List[Dict[str, str]],
filter_active: bool = True
) -> Optional[List[Dict[str, str]]]:
"""Process user list."""
if not users:
return None

if filter_active:
return [u for u in users if u.get('active')]

return users

5. Code Organization

project/
├── src/
│ ├── __init__.py
│ ├── main.py
│ ├── models.py
│ ├── services.py
│ └── utils.py
├── tests/
│ ├── __init__.py
│ ├── test_models.py
│ └── test_services.py
├── requirements.txt
├── README.md
└── .gitignore

6. Code Quality Tools

Black (Code Formatter)

pip install black
black myfile.py

Flake8 (Linter)

pip install flake8
flake8 myfile.py

mypy (Type Checker)

pip install mypy
mypy myfile.py

pylint (Comprehensive Linter)

pip install pylint
pylint myfile.py

7. SOLID Principles

Single Responsibility

# ❌ Bad: Multiple responsibilities
class User:
def __init__(self, name):
self.name = name

def save_to_database(self):
# Database logic
pass

def send_email(self):
# Email logic
pass

# ✅ Good: Separate responsibilities
class User:
def __init__(self, name):
self.name = name

class UserRepository:
def save(self, user):
# Database logic
pass

class EmailService:
def send_email(self, user):
# Email logic
pass

8. DRY (Don't Repeat Yourself)

# ❌ Bad: Repetition
def calculate_circle_area(radius):
return 3.14159 * radius * radius

def calculate_circle_circumference(radius):
return 2 * 3.14159 * radius

# ✅ Good: Reuse constants
import math

def calculate_circle_area(radius):
return math.pi * radius ** 2

def calculate_circle_circumference(radius):
return 2 * math.pi * radius

9. Configuration Management

# config.py
import os
from dataclasses import dataclass

@dataclass
class Config:
DATABASE_URL: str = os.getenv('DATABASE_URL', 'sqlite:///app.db')
API_KEY: str = os.getenv('API_KEY', '')
DEBUG: bool = os.getenv('DEBUG', 'False') == 'True'
MAX_CONNECTIONS: int = int(os.getenv('MAX_CONNECTIONS', '10'))

config = Config()

.env file:

DATABASE_URL=postgresql://user:pass@localhost/dbname
API_KEY=your_api_key_here
DEBUG=True
MAX_CONNECTIONS=20

10. Testing Best Practices

import pytest

# ✅ Good: Clear test names
def test_user_creation_with_valid_data():
user = User("alice", "alice@example.com")
assert user.username == "alice"
assert user.email == "alice@example.com"

def test_user_creation_with_invalid_email_raises_error():
with pytest.raises(ValueError):
User("alice", "invalid-email")

# ✅ Good: Use fixtures
@pytest.fixture
def sample_user():
return User("alice", "alice@example.com")

def test_user_update(sample_user):
sample_user.update_email("newemail@example.com")
assert sample_user.email == "newemail@example.com"

11. Security Best Practices

# ✅ Good: Use environment variables for secrets
import os

API_KEY = os.getenv('API_KEY')

# ✅ Good: Hash passwords
from werkzeug.security import generate_password_hash, check_password_hash

hashed = generate_password_hash('mypassword')
is_valid = check_password_hash(hashed, 'mypassword')

# ✅ Good: Sanitize user input
from html import escape

user_input = escape(user_input)

# ✅ Good: Use parameterized queries
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))

12. Performance Best Practices

# ✅ Good: Use generators for large data
def read_large_file(file_path):
with open(file_path) as file:
for line in file:
yield line.strip()

# ✅ Good: Cache expensive operations
from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_function(n):
# Expensive calculation
return result

# ✅ Good: Use list comprehensions
squares = [x**2 for x in range(1000)]

# ❌ Bad: Manual loop
squares = []
for x in range(1000):
squares.append(x**2)

13. Logging Best Practices

import logging

# ✅ Good: Configure logging properly
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
filename='app.log'
)

logger = logging.getLogger(__name__)

def process_data(data):
logger.info(f"Processing {len(data)} items")
try:
result = perform_operation(data)
logger.info("Processing completed successfully")
return result
except Exception as e:
logger.error(f"Processing failed: {e}", exc_info=True)
raise

14. Code Review Checklist

✅ Follows PEP 8 style guidelines
✅ Has comprehensive docstrings
✅ Includes type hints
✅ Has unit tests with good coverage
✅ Handles errors gracefully
✅ Uses meaningful variable names
✅ No code duplication
✅ No security vulnerabilities
✅ Efficient algorithms and data structures
✅ Logs important events


Summary

✅ Follow PEP 8 for consistent code style
✅ Write comprehensive documentation
✅ Use type hints for clarity
✅ Implement proper error handling
✅ Use code quality tools
✅ Apply SOLID principles
✅ Test thoroughly
✅ Keep security in mind


Next Steps

In Module 35, you'll learn:

  • Python interview preparation
  • Common interview questions
  • Coding challenges
  • Career tips and resources

Practice Exercises

  1. Refactor legacy code to follow best practices
  2. Set up pre-commit hooks with black and flake8
  3. Add comprehensive docstrings to an existing project
  4. Implement type hints throughout a codebase
  5. Create a code review checklist for your team :::