Chapter 4 Code Style and PEP8 Standards

Haiyue
20min

Chapter 4: Code Style and PEP8 Standards

Learning Objectives
  • Understand the importance of PEP8 coding standards
  • Master Pylint’s code style checking capabilities
  • Learn naming conventions and formatting rules
  • Master code structure and organization standards

Key Concepts

PEP8 Standards Overview

PEP8 is Python’s official code style guide that defines coding standards for Python code:

  • Code Layout: Indentation, line length, blank lines
  • Naming Conventions: Naming rules for variables, functions, and classes
  • Comment Standards: How and where to write comments
  • Expressions and Statements: Spacing around operators, line break rules

Pylint Code Style Check Categories

Check CategoryPrefixMain Checks
FORMATC01xxLine length, indentation, spacing
BASICC02xxNaming conventions, docstrings
CLASSESC03xxClass structure, method definitions
DESIGNR09xxCode structure design

Common Code Style Issues

🔄 正在渲染 Mermaid 图表...

Example Code

Format Standard Checks

# format_examples.py - Format standard examples

# === Line Length Check (C0301: line-too-long) ===

# Bad example: Line too long
def bad_long_line_example():
    very_long_variable_name = "This is a very long string that exceeds the maximum line length and should be broken into multiple lines for better readability"
    return very_long_variable_name

# Good example: Proper line breaking
def good_long_line_example():
    very_long_variable_name = (
        "This is a very long string that has been properly "
        "broken into multiple lines for better readability"
    )
    return very_long_variable_name

# Function call line breaking
# Bad example
result = some_function_with_long_name(first_parameter, second_parameter, third_parameter, fourth_parameter, fifth_parameter)

# Good example
result = some_function_with_long_name(
    first_parameter,
    second_parameter,
    third_parameter,
    fourth_parameter,
    fifth_parameter
)

# === Indentation Check (C0326: bad-whitespace) ===

# Bad example: Inconsistent indentation
def bad_indentation():
    if True:
      print("2 spaces")  # Wrong: should use 4 spaces
        print("4 spaces")  # Correct
          print("6 spaces")  # Wrong: over-indented

# Good example: Consistent indentation
def good_indentation():
    if True:
        print("Consistent 4 spaces")
        if True:
            print("Nested 8 spaces")

# === Spacing Check ===

# Bad example: Incorrect spacing
def bad_spacing():
    x=1+2  # Missing spaces
    y = 3*4  # Missing spaces
    z = func( 5,6 )  # Extra spaces

    # Missing space after comma
    my_list=[1,2,3,4]

    # Incorrect colon usage
    my_dict = {'key' : 'value'}

# Good example: Correct spacing
def good_spacing():
    x = 1 + 2  # Spaces around operators
    y = 3 * 4  # Spaces around operators
    z = func(5, 6)  # Correct function call format

    # Space after comma
    my_list = [1, 2, 3, 4]

    # Correct colon usage
    my_dict = {'key': 'value'}

# === Blank Line Standards (C0303: trailing-whitespace, C0305: trailing-newlines) ===

# Blank lines around class definitions
class MyClass:
    """Example class"""

    def method_one(self):
        """First method"""
        pass

    def method_two(self):
        """Second method"""
        pass


class AnotherClass:
    """Another class"""
    pass


# Blank lines around function definitions
def top_level_function():
    """Top-level function"""
    pass


def another_top_level_function():
    """Another top-level function"""
    pass

# === Import Format Check ===

# Bad example: Mixed import order
import sys
from mymodule import MyClass
import os
from collections import defaultdict

# Good example: Correct import order
import os
import sys

from collections import defaultdict

from mymodule import MyClass

Naming Convention Checks

# naming_conventions.py - Naming convention examples

# === Variable Naming (C0103: invalid-name) ===

# Bad example: Non-standard naming
camelCaseVariable = "Should use snake_case"
SHOULD_BE_CONSTANT = "variable"  # Not a constant but using uppercase
x = "Unclear variable name"
very_very_very_long_variable_name_that_is_too_descriptive = "Too long"

# Good example: Standard naming
snake_case_variable = "Correct variable naming"
USER_AGENT = "Correct constant naming"
user_count = "Meaningful variable name"
temp_file_path = "Descriptive but not too long variable name"

# === Function Naming ===

# Bad example
def calculateSum(a, b):  # Should use snake_case
    return a + b

def func():  # Name not descriptive enough
    pass

def do_something_very_specific_and_complex_operation():  # Too long
    pass

# Good example
def calculate_sum(a, b):
    """Calculate the sum of two numbers"""
    return a + b

def process_data():
    """Process data"""
    pass

def validate_user_input():
    """Validate user input"""
    pass

# === Class Naming ===

# Bad example
class myClass:  # Should use PascalCase
    pass

class user_manager:  # Should use PascalCase
    pass

class HTTPSConnectionManager:  # Acceptable abbreviation
    pass

# Good example
class MyClass:
    """My class"""
    pass

class UserManager:
    """User manager"""
    pass

class DatabaseConnection:
    """Database connection class"""
    pass

# === Constant Naming ===

# Bad example
maxRetries = 3  # Should be all uppercase
default_timeout = 30  # Constants should be uppercase

# Good example
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30
API_BASE_URL = "https://api.example.com"

# === Private Attribute and Method Naming ===

class ExampleClass:
    """Example class"""

    def __init__(self):
        self.public_attribute = "Public attribute"
        self._protected_attribute = "Protected attribute"
        self.__private_attribute = "Private attribute"

    def public_method(self):
        """Public method"""
        return "public"

    def _protected_method(self):
        """Protected method"""
        return "protected"

    def __private_method(self):
        """Private method"""
        return "private"

# === Module and Package Naming ===

# Good module name examples:
# user_manager.py
# database_connection.py
# api_client.py

# Bad module name examples:
# UserManager.py  # Should not use PascalCase
# database-connection.py  # Should not use hyphens
# apiClient.py  # Should not use camelCase

# === Special Naming Patterns ===

# Loop variables
for i in range(10):  # Single letter variables acceptable in loops
    print(i)

for index, value in enumerate(['a', 'b', 'c']):  # More descriptive names are better
    print(f"{index}: {value}")

# Exception handling
try:
    risky_operation()
except ValueError as e:  # 'e' is acceptable exception variable name
    print(f"Error: {e}")

except Exception as exc:  # 'exc' is also acceptable
    print(f"Unexpected error: {exc}")

# Unused variables
def function_with_unused_parameter(used_param, _unused_param):
    """Use underscore prefix for unused parameters"""
    return used_param * 2

# Temporary variables
temp_data = process_raw_data()  # 'temp_' prefix indicates temporary variable
temp_file = create_temporary_file()

Docstring Standards

# docstring_examples.py - Docstring examples

"""
Module-level docstring

This module contains best practice examples for docstrings.
Follows PEP 257 and Google style guide.
"""

# === Module Docstring (C0114: missing-module-docstring) ===

# Every module should have a docstring

# === Class Docstring (C0115: missing-class-docstring) ===

class DocumentedClass:
    """
    A well-documented class

    This class demonstrates how to write clear, useful docstrings.
    It follows the Google-style docstring format.

    Attributes:
        name (str): The name of the instance
        value (int): The numeric value of the instance

    Example:
        Create an instance and use it:

        >>> obj = DocumentedClass("example", 42)
        >>> obj.get_description()
        'example: 42'
    """

    def __init__(self, name: str, value: int):
        """
        Initialize DocumentedClass instance

        Args:
            name (str): The name of the instance
            value (int): The numeric value of the instance

        Raises:
            ValueError: When value is negative
        """
        if value < 0:
            raise ValueError("Value must be non-negative")

        self.name = name
        self.value = value

    def get_description(self) -> str:
        """
        Get instance description

        Returns:
            str: Formatted description string

        Example:
            >>> obj = DocumentedClass("test", 10)
            >>> obj.get_description()
            'test: 10'
        """
        return f"{self.name}: {self.value}"

    def calculate_square(self) -> int:
        """
        Calculate the square of value

        Returns:
            int: The squared value

        Note:
            This method does not modify the instance state
        """
        return self.value ** 2

    @classmethod
    def from_string(cls, data: str) -> 'DocumentedClass':
        """
        Create instance from string

        Args:
            data (str): String in format "name:value"

        Returns:
            DocumentedClass: Newly created instance

        Raises:
            ValueError: When string format is incorrect

        Example:
            >>> obj = DocumentedClass.from_string("example:42")
            >>> obj.name
            'example'
            >>> obj.value
            42
        """
        try:
            name, value_str = data.split(':')
            value = int(value_str)
            return cls(name.strip(), value)
        except (ValueError, IndexError) as e:
            raise ValueError(f"Invalid format: {data}") from e

    @staticmethod
    def validate_name(name: str) -> bool:
        """
        Validate if name is valid

        Args:
            name (str): Name to validate

        Returns:
            bool: True if name is valid, False otherwise

        Note:
            Valid names must be non-empty strings containing only letters and numbers
        """
        return name.isalnum() and len(name) > 0

# === Function Docstring (C0116: missing-function-docstring) ===

def complex_calculation(
    base: float,
    exponent: int,
    precision: int = 2,
    use_cache: bool = True
) -> float:
    """
    Perform complex mathematical calculation

    This function calculates base raised to the exponent power,
    and rounds the result to the specified precision.

    Args:
        base (float): The base number
        exponent (int): The exponent
        precision (int, optional): Decimal precision. Defaults to 2.
        use_cache (bool, optional): Whether to use caching. Defaults to True.

    Returns:
        float: Calculation result, rounded to specified precision

    Raises:
        TypeError: When parameter types are incorrect
        ValueError: When precision is negative

    Example:
        >>> complex_calculation(2.0, 3)
        8.0
        >>> complex_calculation(2.5, 2, precision=3)
        6.25

    Note:
        This function may be slow on first call due to cache initialization
    """
    if not isinstance(base, (int, float)):
        raise TypeError("Base must be a number")

    if not isinstance(exponent, int):
        raise TypeError("Exponent must be an integer")

    if precision < 0:
        raise ValueError("Precision must be non-negative")

    result = base ** exponent
    return round(result, precision)

# === Short Function Docstrings ===

def add(a: int, b: int) -> int:
    """Return the sum of two integers"""
    return a + b

def is_even(number: int) -> bool:
    """Check if number is even"""
    return number % 2 == 0

# === Bad Docstring Examples ===

def bad_docstring_example():
    """this function does stuff"""  # Bad: not descriptive, no capitalization
    pass

def another_bad_example(x, y):
    """
    adds x and y
    """  # Bad: no parameter and return value description
    return x + y

class BadClass:
    """a class"""  # Bad: too simple

    def method(self):
        """does something"""  # Bad: not specific enough
        pass

# === Good Docstring Examples ===

def good_docstring_example(data: list) -> dict:
    """
    Process data list and return statistics

    Args:
        data (list): The data list to process

    Returns:
        dict: Dictionary containing statistics with 'count', 'sum', 'average' keys

    Example:
        >>> good_docstring_example([1, 2, 3, 4, 5])
        {'count': 5, 'sum': 15, 'average': 3.0}
    """
    if not data:
        return {'count': 0, 'sum': 0, 'average': 0}

    total = sum(data)
    count = len(data)
    average = total / count

    return {
        'count': count,
        'sum': total,
        'average': average
    }

class GoodClass:
    """
    A class demonstrating good documentation practices

    This class provides data processing and analysis functionality,
    demonstrating how to write clear, useful docstrings.

    Attributes:
        data (list): The stored data list
        processed (bool): Whether the data has been processed

    Example:
        >>> processor = GoodClass([1, 2, 3])
        >>> processor.process()
        >>> processor.get_result()
        {'processed': True, 'count': 3}
    """

    def __init__(self, data: list):
        """
        Initialize data processor

        Args:
            data (list): Initial data to process
        """
        self.data = data
        self.processed = False

    def process(self) -> None:
        """
        Process stored data

        This method marks data as processed.
        In actual applications, this would contain complex data processing logic.
        """
        # Actual processing logic would go here
        self.processed = True

    def get_result(self) -> dict:
        """
        Get processing result

        Returns:
            dict: Dictionary containing processing status and data information

        Raises:
            RuntimeError: When data has not been processed yet
        """
        if not self.processed:
            raise RuntimeError("Data must be processed first")

        return {
            'processed': self.processed,
            'count': len(self.data)
        }

Code Organization and Structure Standards

# code_organization.py - Code organization standard examples

"""
Code Organization and Structure Standard Examples

This module demonstrates how to organize Python code to comply with
PEP8 and Pylint requirements. Includes import organization, class and
function organization, and overall code structure.
"""

# === Import Organization (PEP8 Standard) ===

# 1. Standard library imports
import os
import sys
from pathlib import Path
from typing import Dict, List, Optional, Union

# 2. Third-party library imports
import requests
from flask import Flask

# 3. Local application imports
from myapp.models import User
from myapp.utils import format_date

# === Constant Definitions ===

MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30
API_VERSION = "v1"

# === Module-level Variables ===

app = Flask(__name__)
_cache = {}  # Private module variable

# === Exception Class Definitions ===

class CustomError(Exception):
    """Custom exception class"""
    pass

class ValidationError(CustomError):
    """Validation error"""
    pass

# === Main Class Definitions ===

class DataProcessor:
    """
    Data processor class

    Responsible for processing and validating input data, providing data transformation functionality.
    """

    def __init__(self, config: dict):
        """
        Initialize data processor

        Args:
            config (dict): Configuration parameters
        """
        self.config = config
        self._processed_count = 0

    def process(self, data: List[dict]) -> List[dict]:
        """
        Process data list

        Args:
            data (List[dict]): Data to process

        Returns:
            List[dict]: Processed data
        """
        result = []
        for item in data:
            processed_item = self._process_single_item(item)
            result.append(processed_item)
            self._processed_count += 1

        return result

    def _process_single_item(self, item: dict) -> dict:
        """
        Process single data item

        Args:
            item (dict): Single data item

        Returns:
            dict: Processed data item
        """
        # Processing logic
        return {**item, 'processed': True}

    @property
    def processed_count(self) -> int:
        """Get the number of processed items"""
        return self._processed_count

# === Helper Functions ===

def validate_input(data: dict) -> bool:
    """
    Validate input data

    Args:
        data (dict): Data to validate

    Returns:
        bool: Validation result
    """
    required_fields = ['name', 'type']
    return all(field in data for field in required_fields)

def format_output(data: dict) -> str:
    """
    Format output data

    Args:
        data (dict): Data to format

    Returns:
        str: Formatted string
    """
    return f"{data.get('name', 'Unknown')}: {data.get('type', 'Unknown')}"

# === Main Function ===

def main():
    """Main function"""
    config = {
        'timeout': DEFAULT_TIMEOUT,
        'retries': MAX_RETRIES
    }

    processor = DataProcessor(config)
    sample_data = [
        {'name': 'item1', 'type': 'A'},
        {'name': 'item2', 'type': 'B'}
    ]

    result = processor.process(sample_data)
    print(f"Processed {len(result)} items")

# === Module Execution Entry Point ===

if __name__ == "__main__":
    main()
Code Style Best Practices
  1. Consistency: Maintain consistent code style throughout the project
  2. Readability: Prioritize code readability over brevity
  3. Tool Integration: Use tools like black, isort for automated formatting
  4. Team Conventions: Establish and strictly enforce team coding style conventions
  5. Gradual Improvement: For large projects, improve code style progressively
Common Style Issues
  1. Over-optimization: Don’t over-complicate code to comply with standards
  2. Tool Conflicts: Ensure different tool settings are compatible with each other
  3. Legacy Code: Consider impact scope when dealing with legacy code
  4. Performance Impact: Some style requirements may have slight performance impact

Code style and PEP8 standards are the foundation of Python development. Good code style not only improves readability but also reduces friction in team collaboration.