Python Best Practices¶
This guide covers essential Python programming best practices for writing clean, efficient, and maintainable code following PEP 8 and Pythonic conventions.
🎯 Core Python Best Practices¶
Naming Conventions (PEP 8)¶
- Variables/Functions: snake_case (e.g.,
student_name,calculate_total()) - Classes: PascalCase (e.g.,
StudentRecord,DatabaseManager) - Constants: UPPER_SNAKE_CASE (e.g.,
MAX_RETRY_ATTEMPTS,DEFAULT_TIMEOUT) - Private Members: Leading underscore (e.g.,
_internal_method,__private_var) - Modules: lowercase with underscores (e.g.,
student_utils.py)
Code Organization¶
- Docstrings: Use triple quotes for function/class documentation
- Type Hints: Add type annotations for better code clarity
- Import Organization: Group imports (standard library, third-party, local)
- Function Length: Keep functions short and focused on single responsibility
Pythonic Idioms¶
- List Comprehensions: Prefer over explicit loops
- Context Managers: Use
withfor resource management - Generators: Use
yieldfor memory-efficient iteration - Decorators: Use for cross-cutting concerns
🔧 Python-Specific Techniques¶
Input/Output¶
# Good: Using context managers and exception handling
def get_user_input():
"""Get user input with proper error handling."""
try:
with open('data.txt', 'r') as file:
return file.read()
except FileNotFoundError:
print("File not found, using default value")
return "default"
except IOError as e:
print(f"Error reading file: {e}")
return None
# Bad: No error handling
def get_user_input_bad():
file = open('data.txt', 'r')
return file.read() # File never closed!
List Operations¶
# Good: List comprehensions and built-in functions
numbers = [1, 2, 3, 4, 5]
squares = [x**2 for x in numbers] # List comprehension
even_numbers = [x for x in numbers if x % 2 == 0] # Filtering
total = sum(numbers) # Built-in function
# Bad: Manual loops for simple operations
squares_bad = []
for x in numbers:
squares_bad.append(x**2)
total_bad = 0
for x in numbers:
total_bad += x
String Handling¶
# Good: f-strings and string methods
name = "John"
age = 25
message = f"My name is {name} and I'm {age} years old"
# Good: String methods for manipulation
text = " Hello World "
cleaned = text.strip().lower().replace(' ', '_')
# Bad: String concatenation with +
message_bad = "My name is " + name + " and I'm " + str(age) + " years old"
Function Design¶
# Good: Type hints, docstrings, single responsibility
from typing import List, Optional
def calculate_average(numbers: List[float]) -> Optional[float]:
"""
Calculate the average of a list of numbers.
Args:
numbers: List of numeric values
Returns:
Average value or None if list is empty
"""
if not numbers:
return None
return sum(numbers) / len(numbers)
# Bad: No type hints, unclear purpose
def calc(data):
# What does this function do?
return sum(data) / len(data)
⚠️ Common Python Pitfalls¶
Mutable Default Arguments¶
# Bad: Mutable default argument
def add_item(item, items=[]): # Same list used across calls!
items.append(item)
return items
# Good: Use None as default
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
Variable Scope Issues¶
# Bad: Modifying loop variable
numbers = [1, 2, 3, 4, 5]
for i in range(len(numbers)):
if numbers[i] % 2 == 0:
numbers.remove(i) # Modifying list while iterating!
# Good: Create new list or use list comprehension
numbers = [1, 2, 3, 4, 5]
even_numbers = [x for x in numbers if x % 2 == 0]
Comparison with None¶
# Bad: Using == for None comparison
if value == None: # Should use 'is'
pass
# Good: Use 'is' for identity comparison
if value is None:
pass
# Better: Use truthiness for general cases
if not value:
pass
🚀 Performance Optimization¶
Memory Efficiency¶
# Good: Generators for large datasets
def process_large_file(filename):
"""Process large file line by line."""
with open(filename, 'r') as file:
for line in file: # Generator, memory efficient
yield process_line(line)
# Bad: Loading entire file into memory
def process_large_file_bad(filename):
with open(filename, 'r') as file:
lines = file.readlines() # Loads entire file!
return [process_line(line) for line in lines]
Efficient Data Structures¶
# Good: Use appropriate data structures
# Set for membership testing (O(1))
valid_colors = {'red', 'green', 'blue'}
if color in valid_colors: # Fast lookup
pass
# Bad: List for membership testing (O(n))
valid_colors_bad = ['red', 'green', 'blue']
if color in valid_colors_bad: # Slow lookup
pass
String Operations¶
# Good: Join for string concatenation
words = ['hello', 'world', 'python']
sentence = ' '.join(words) # Efficient
# Bad: Repeated string concatenation
sentence_bad = ''
for word in words:
sentence_bad += word + ' ' # Creates new string each time
🛠️ Error Handling¶
Exception Handling Best Practices¶
# Good: Specific exceptions and proper cleanup
def read_config(filename):
"""Read configuration file with proper error handling."""
try:
with open(filename, 'r') as file:
return json.load(file)
except FileNotFoundError:
logging.error(f"Config file not found: {filename}")
return {}
except json.JSONDecodeError as e:
logging.error(f"Invalid JSON in config: {e}")
return {}
except Exception as e:
logging.error(f"Unexpected error reading config: {e}")
raise
# Bad: Catching all exceptions
def read_config_bad(filename):
try:
with open(filename, 'r') as file:
return json.load(file)
except: # Too broad!
return {}
📚 Testing Best Practices¶
Unit Testing with pytest¶
# Good: Descriptive test names and assertions
import pytest
from calculator import add
def test_add_positive_numbers():
"""Test adding two positive numbers."""
result = add(2, 3)
assert result == 5
def test_add_negative_numbers():
"""Test adding two negative numbers."""
result = add(-2, -3)
assert result == -5
def test_add_with_none_raises_exception():
"""Test that add raises TypeError for None input."""
with pytest.raises(TypeError):
add(None, 5)
📦 Project Structure¶
Recommended Directory Layout¶
project_name/
├── src/
│ └── project_name/
│ ├── __init__.py
│ ├── main.py
│ └── utils.py
├── tests/
│ ├── __init__.py
│ ├── test_main.py
│ └── test_utils.py
├── docs/
├── requirements.txt
├── setup.py
└── README.md
Requirements Management¶
📚 Related Resources¶
- PEP 8 Style Guide - Official Python style guide
- Real Python - Comprehensive Python tutorials
- Python Documentation - Official Python docs
- Effective Python - Best practices book
🔗 Related Guides¶
- Python Common Mistakes - Avoid frequent errors
- Python Performance Tips - Optimization techniques
- Python Testing Frameworks - Testing with pytest and unittest
- Python Resources - Learning materials and tools
🔗 Common Programming Best Practices¶
- General Programming Principles - Universal concepts
- Code Organization - Structuring your code
- Debugging Strategies - Troubleshooting techniques