Skip to main content

Python Code Style

Good code isn't just about making your program work; it's about making it work well and ensuring others (including future you) can understand it. Python's code style conventions help developers write consistent, readable code that's easier to maintain and collaborate on.

Introduction to Python Code Style

Python places a significant emphasis on code readability. This philosophy is captured in the famous quote from "The Zen of Python":

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Readability counts.

Following established style conventions helps you write code that adheres to these principles. The most important Python style guide is PEP 8, which provides coding conventions for the Python standard library and is widely adopted in the Python community.

PEP 8 Guidelines: The Python Style Bible

Indentation

Python uses indentation for code blocks. The standard is to use 4 spaces per indentation level:

python
# Correct indentation
def calculate_average(numbers):
total = sum(numbers)
count = len(numbers)
return total / count if count > 0 else 0

Maximum Line Length

Limit lines to 79 characters for code and 72 for comments and docstrings:

python
# Too long line
result = some_function_with_a_really_long_name(parameter1, parameter2, parameter3, parameter4, parameter5, parameter6, parameter7)

# Better - using line breaks
result = some_function_with_a_really_long_name(
parameter1,
parameter2,
parameter3,
parameter4,
parameter5,
parameter6,
parameter7
)

Imports

Organize imports in the following order:

  1. Standard library imports
  2. Related third-party imports
  3. Local application/library-specific imports

Separate each group with a blank line:

python
# Standard library imports
import os
import sys
from datetime import datetime

# Third-party imports
import numpy as np
import pandas as pd

# Local imports
from mypackage import utils
from mymodule.helpers import format_data

Whitespace

Use whitespace judiciously:

python
# Correct
x = 1
y = 2
long_variable = 3

# Correct function definition
def function(x, y=0):
return x + y

# Correct list comprehension
squares = [x**2 for x in range(10)]

Avoid:

python
# Avoid extraneous whitespace
x=1 # Missing spaces around = operator
function( x ) # Unnecessary whitespace inside parentheses

Naming Conventions

Variables and Functions

Use snake_case for variable and function names:

python
# Good
student_name = "John"
first_score = 85
calculate_average = lambda x, y: (x + y) / 2

# Avoid
StudentName = "John" # Looks like a class
FirstScore = 85 # Not following snake_case

Classes

Use PascalCase (also called CapWords or CamelCase) for class names:

python
# Good
class StudentRecord:
pass

class DatabaseConnection:
pass

# Avoid
class student_record: # Should be PascalCase
pass

Constants

Use UPPER_CASE_WITH_UNDERSCORES for constants:

python
# Good
MAX_STUDENTS = 30
DATABASE_URL = "postgresql://user:pass@localhost/db"

# Avoid
maxStudents = 30 # Should be uppercase with underscores

Code Structure and Organization

Function and Method Length

Keep functions focused and concise. A good rule of thumb is that a function should do one thing and do it well:

python
# Too complex function
def process_student_data(students):
# 50+ lines of code doing multiple things
pass

# Better approach: split into focused functions
def validate_student_data(students):
# Validation logic
pass

def calculate_student_statistics(students):
# Statistics calculation
pass

def format_student_report(students, statistics):
# Report formatting
pass

def process_student_data(students):
validated_data = validate_student_data(students)
statistics = calculate_student_statistics(validated_data)
return format_student_report(validated_data, statistics)

Comments and Documentation

Use comments sparingly but effectively:

python
# This is a regular comment

"""
This is a multiline docstring that explains
what this module does in more detail.
"""

def calculate_gpa(grades):
"""
Calculate the Grade Point Average from a list of grades.

Args:
grades (list): A list of numeric grades

Returns:
float: The calculated GPA

Raises:
ValueError: If grades list is empty
"""
if not grades:
raise ValueError("Grades list cannot be empty")
return sum(grades) / len(grades)

Real-World Example: Building a Contact Manager

Let's apply these style guidelines to build a simple contact manager:

python
"""
Contact Manager Module

This module provides functionality to manage contacts.
It demonstrates proper Python code style and organization.
"""

import json
from datetime import datetime
from typing import Dict, List, Optional


# Constants
FILE_PATH = "contacts.json"
MAX_CONTACTS = 100
REQUIRED_FIELDS = ["name", "email"]


class Contact:
"""Represents a contact with name, email and other optional details."""

def __init__(self, name: str, email: str, phone: Optional[str] = None):
"""
Initialize a new contact.

Args:
name: Contact's full name
email: Contact's email address
phone: Contact's phone number (optional)
"""
self.name = name
self.email = email
self.phone = phone
self.created_at = datetime.now()

def to_dict(self) -> Dict:
"""Convert contact to dictionary for storage."""
return {
"name": self.name,
"email": self.email,
"phone": self.phone,
"created_at": self.created_at.isoformat()
}

@classmethod
def from_dict(cls, data: Dict) -> 'Contact':
"""Create a Contact object from dictionary data."""
contact = cls(
name=data["name"],
email=data["email"],
phone=data.get("phone")
)
if "created_at" in data:
contact.created_at = datetime.fromisoformat(data["created_at"])
return contact


class ContactManager:
"""Manages a collection of contacts with CRUD operations."""

def __init__(self, file_path: str = FILE_PATH):
"""
Initialize the contact manager.

Args:
file_path: Path to the JSON file for contact storage
"""
self.file_path = file_path
self.contacts = self._load_contacts()

def _load_contacts(self) -> List[Contact]:
"""Load contacts from storage file."""
try:
with open(self.file_path, 'r') as file:
contacts_data = json.load(file)
return [Contact.from_dict(data) for data in contacts_data]
except (FileNotFoundError, json.JSONDecodeError):
return []

def save_contacts(self) -> None:
"""Save contacts to storage file."""
contacts_data = [contact.to_dict() for contact in self.contacts]
with open(self.file_path, 'w') as file:
json.dump(contacts_data, file, indent=2)

def add_contact(self, contact: Contact) -> bool:
"""
Add a new contact.

Args:
contact: The contact to add

Returns:
bool: True if added successfully, False otherwise
"""
if len(self.contacts) >= MAX_CONTACTS:
return False

# Check for duplicate email
if any(c.email == contact.email for c in self.contacts):
return False

self.contacts.append(contact)
self.save_contacts()
return True

def find_contact_by_email(self, email: str) -> Optional[Contact]:
"""
Find a contact by email.

Args:
email: The email to search for

Returns:
Contact or None: The found contact or None
"""
for contact in self.contacts:
if contact.email.lower() == email.lower():
return contact
return None

def delete_contact(self, email: str) -> bool:
"""
Delete a contact by email.

Args:
email: The email of the contact to delete

Returns:
bool: True if deleted, False if not found
"""
contact = self.find_contact_by_email(email)
if contact:
self.contacts.remove(contact)
self.save_contacts()
return True
return False


# Example usage
if __name__ == "__main__":
# Create manager and add some contacts
manager = ContactManager()

# Add a new contact
new_contact = Contact(
name="John Doe",
email="[email protected]",
phone="555-1234"
)

success = manager.add_contact(new_contact)
print(f"Contact added: {success}")

# Find a contact
found_contact = manager.find_contact_by_email("[email protected]")
if found_contact:
print(f"Found: {found_contact.name}")

# Delete a contact
deleted = manager.delete_contact("[email protected]")
print(f"Contact deleted: {deleted}")

This example showcases:

  1. Clear module docstrings and function docstrings
  2. Proper import organization
  3. Consistent naming conventions
  4. Type hints for better code understanding
  5. Logical code organization with classes and methods
  6. Constants at the module level
  7. Clean function definitions with appropriate spacing

Tools for Maintaining Code Style

Linters and Formatters

Several tools can help you maintain consistent code style:

  1. Flake8: A linter that checks your code against PEP 8 and finds common bugs.
  2. pylint: A more comprehensive linter that also checks for code smells.
  3. black: An opinionated code formatter that automatically formats your code.
  4. isort: A utility that sorts imports alphabetically and separates them into sections.

Example of using Black to format code:

bash
# Install black
pip install black

# Format a file
black your_file.py

# Format all Python files in a directory
black your_directory/

IDE Integration

Most modern IDEs support PEP 8 checking and can highlight style violations:

  • PyCharm: Has built-in PEP 8 checking and can format code automatically
  • VS Code: With the Python extension, provides linting and formatting capabilities
  • Jupyter Notebooks: Can be configured to use linters and formatters

Summary

Following Python's code style conventions leads to:

  • More readable and maintainable code
  • Easier collaboration with other developers
  • Fewer bugs and better code quality
  • Code that aligns with Python's philosophy

The most important aspects of Python code style are:

  • Using consistent indentation (4 spaces)
  • Following naming conventions (snake_case for variables/functions, PascalCase for classes)
  • Organizing imports properly
  • Writing clear, concise docstrings and comments
  • Keeping functions focused and code modular

As you develop your Python skills, incorporating these style practices will make you a better programmer and more effective team member.

Additional Resources

Exercises

  1. Take a small Python script you've written and format it according to PEP 8 guidelines.
  2. Install and run a linter (like flake8) on your code to identify style issues.
  3. Refactor a function that does multiple things into smaller, more focused functions.
  4. Add proper docstrings to a Python module you've created.
  5. Create a .pylintrc or setup.cfg file for one of your projects that configures your preferred code style checks.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)