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 :::