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
- Refactor legacy code to follow best practices
- Set up pre-commit hooks with black and flake8
- Add comprehensive docstrings to an existing project
- Implement type hints throughout a codebase
- Create a code review checklist for your team :::