PEP 8 is Python’s official style guide, providing a set of rules and coding conventions to standardize code across different Python projects and developers. It covers topics including naming conventions, code layout, whitespace usage, programming practices, and documentation. Following PEP 8 guidelines helps create Python code that is professional, simple, consistent, and easy to understand. The benefits of PEP 8-compliant code include:
- Improved readability and maintainability of code.
- Consistent styling across developers and teams.
- Professional look and feel of code.
- Easy to understand logic and program flow.
- Helps avoid common errors and bugs.
- Promotes good programming practices.
- Enables better collaboration between developers.
This guide will break down the key components of PEP 8, providing actionable tips and corrected Python code examples for writing clean code that adheres to its standards. Both novice and experienced Python programmers can use this as a practical reference when writing new code or refactoring existing code. The intended audience for this article is Python developers of all skill levels who want to write more readable, maintainable, and collaborative code.
Naming Conventions
PEP 8 contains specific guidelines for naming variables, functions, classes, modules, etc. Using clear, descriptive, and consistent naming helps improve code understandability.
Use lowercase with underscores
Function, variable, and module names should be in all lowercase, using underscores to separate words:
# Correct conventionsmy_variable = 'Hello'
def calculate_sum(numbers): pass # Added pass statement to make the function valid
import helper_moduleClass names should be in CamelCase format:
# Class name uses CamelCaseclass MyClassName: passAvoid single letter names
Single letter variables like ‘x’ or ‘y’ can be confusing. Use more descriptive names:
# Avoidx = 1y = 2
# Preferrednumber_of_items = 1item_counter = 2Name booleans and constants appropriately
Booleans should be named using ‘is’ or ‘has’ prefixes. Constants should be all caps with underscores:
# Booleansis_active = Truehas_permissions = False
# ConstantsMAX_SIZE = 100CONNECTION_TIMEOUT = 50Use verbs for function names
Function names should clearly describe the action being performed and use a verb to start:
# Good function namesdef get_user_data(): pass
def calculate_average(numbers): passCode Layout and Whitespace
Proper spacing, indentation, and placement of code blocks improves code organization and visual appearance.
Indentation
- Use 4 spaces per indentation level (no tabs).
- Indent after function definitions, loops, if statements etc.
- Continued lines should align vertically using the same indentation or using a hanging indent.
# Aligned indentation
def process_user(user): if user.is_active(): print('Active user found') # Removed undefined functions for clarity # send_notification() # update_status('Active') pass
for item in user.list_of_items: # Removed undefined functions for clarity # process_item(item) # save_changes() passLine length
- Limit lines to 79 characters for code and 72 characters for comments and docstrings.
- Use parentheses, hanging indents, and line continuation for long lines.
# Use parenthesesdef long_function_call_with_many_args(arg1, arg2, arg3, arg4, arg5): pass
# Hanging indentslong_string = ('This is a very long string that goes over ' 'the character limit')
# Line continuationlong_calculation = (some_value + some_other_value - some_condition)Whitespace
- Surround operators and assignments with spaces for readability.
- Do not use extraneous whitespace inside parentheses, brackets, or braces.
# Spaces around operatorsx = 1 + 2value = (3 * 4) - 5
# No extra whitespacemy_list = [1, 2, 3]my_dict = {'key': 'value'}
# Spaces separating itemsif x == 1 and y == 2: passBlank lines
- Use blank lines sparingly to separate logical sections of code.
- Two blank lines between top-level function and class definitions.
- One blank line between method definitions in a class.
- No blank lines inside brackets, braces, or parentheses.
# Logical blank line spacing
def process_data(): data = get_data()
# Blank line results = analyze(data)
return results
class MyClass:
def __init__(self): pass
# Blank line def method1(self): pass
def method2(self): passImports
Imports should be organized and located at the top of the file below module documentation.
Group imports
Imports should be grouped in the following order:
- Standard library imports
- Third-party package imports
- Local application imports
Each grouping should be separated by one blank line:
# Standard libraryimport osimport sysimport json
# Third-partyimport numpy as npimport pandas as pd
# Local importsfrom . import helperfrom .utils.converters import convert_to_floatAvoid relative package imports (generally)
While relative imports have their use cases within a package, PEP 8 generally recommends absolute imports for better clarity and to avoid potential issues with the import path. For imports within the same package, explicit relative imports are acceptable (e.g., from .module import something).
Limit line length
Break long import statements across multiple lines, ideally limiting to 79 characters. Use parentheses and hanging indents when needed:
# Long importsfrom third_party_lib.specific_package import ( VeryLongModuleName, AnotherVeryLongName,)
from my_local_package.utils.modules import ( my_module, my_other_module, helper_functions,)Programming Practices
PEP 8 contains several guidelines for best practices when coding in Python:
Avoid extraneous code
Remove any code that does not get used - old debug statements, unnecessary comments, unused variables, or imports, etc. They add clutter and increase file size needlessly.
Use meaningful comments
Comments should explain complex code or algorithms. Avoid extraneous comments that just restate the code. Strive for comments that explain the why rather than just the what.
# Good comments
# This converts the dataframe columns to numeric using partial function application# to ensure consistent data types for analysis.# cols_to_numeric = partial(convert_cols, df=dataframe, cols=['col1', 'col2']) # Requires import and definition of partial and convert_cols
# Bad commentsx = x + 1 # Increment x by 1Modularize code into functions
Break code into logical chunks using functions for better organization. Functions should ideally do one thing and do it well.
Avoid deeply nested control flows
Nested if statements, loops, and comprehensions can make code hard to read. Use functions to encapsulate the logic or consider alternative control flow structures.
Use context managers
Use context managers like ‘with’ for resource handling instead of try/finally blocks. This ensures resources are properly cleaned up even if errors occur.
# Context managerwith open('file.txt') as f: data = f.read()
# Without context managerf = open('file.txt')try: data = f.read()finally: f.close()Prefer enumerate over range when you need the index
Use enumerate() to get index values when iterating through a sequence, as it’s more Pythonic and readable.
list_data = ["a", "b", "c"]for i in range(len(list_data)): print(i, list_data[i])
# Betterfor i, val in enumerate(list_data): print(i, val)Documentation
Proper documentation makes code easier to use and understand. PEP 8 recommends using:
Docstrings
Docstrings provide help text and usage information. Use triple double-quotes and include Args/Returns/Raises sections for public modules, functions, classes, and methods.
import math
def calculate_area(radius): """Calculate the area of a circle.
Args: radius (int or float): The radius of the circle.
Returns: float: The calculated circle area. """ area = math.pi * radius**2 return areaComments
Comments improve understandability and provide explanation for specific lines or sections of code. Use them sparingly inside functions/classes to explain non-obvious logic.
def process_data(data): # Ensure data is sorted by timestamp before processing data.sort(key=lambda x: x['timestamp']) # ... rest of the processing logic passVersion Docstrings
While not a strict PEP 8 requirement, including a module-level docstring with version information, author, and creation date is a good practice for maintainability and tracking.
"""my_module.py
Performs X task.
Version: 1.0Author: Mark Anthony LlegoCreated: 2021-01-10"""Tools for PEP 8 Compliance
Linters
Using a linter like flake8 (which includes pycodestyle, pyflakes, and mccabe) will automatically check your code and catch PEP 8 issues/violations. Configure your editor or CI/CD pipeline to run a linter regularly to enforce compliance.
Auto-formatters
Tools like Black and autopep8 can automatically reformat your code to conform to PEP 8 standards. Black is more opinionated and makes fewer configuration choices, leading to consistent formatting across projects.
Conclusion
PEP 8 contains comprehensive style guidelines for writing clean, readable, and professional Python code. Following its conventions improves collaboration between developers working on shared codebases. This guide provided actionable tips and examples for writing PEP 8-compliant code covering topics like naming, whitespace, imports, practices, and documentation. Writing high-quality Python code using these standards ensures code maintainability, consistency, and better teamwork.
Example Code
Here is an example Python script demonstrating various PEP 8 conventions:
"""example.py
Simple script with PEP 8 examples.
Author: Mark Anthony LlegoCreated: 2021-01-10"""
# Standard libraryimport jsonimport mathimport sys
# Third partyimport numpy as np
# Local applicationfrom mypackage.utils import helper
# CONSTANTSPI = 3.141592653
# Function with docstringdef calculate_area(radius): """Calculate the area of a circle.
Args: radius (int or float): The circle radius.
Returns: float: The circle area. """ area = PI * radius**2 return area
# Class with docstringclass Circle: """Class to represent a geometric circle."""
def __init__(self, radius): """Initialize a Circle object.""" self.radius = radius
def area(self): """Compute the circle's area.""" return calculate_area(self.radius)
def main(): """Main function of the script.""" # Open file using a context manager try: with open('data.json') as f: data = json.load(f) print(data) except FileNotFoundError: print("Error: data.json not found.")
# Create instance of the Circle class c = Circle(5) print(f'Area: {c.area():.2f}')
if __name__ == '__main__': main()This example applies several PEP 8 guidelines discussed in this guide.