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 Category | Prefix | Main Checks |
|---|---|---|
| FORMAT | C01xx | Line length, indentation, spacing |
| BASIC | C02xx | Naming conventions, docstrings |
| CLASSES | C03xx | Class structure, method definitions |
| DESIGN | R09xx | Code 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
- Consistency: Maintain consistent code style throughout the project
- Readability: Prioritize code readability over brevity
- Tool Integration: Use tools like black, isort for automated formatting
- Team Conventions: Establish and strictly enforce team coding style conventions
- Gradual Improvement: For large projects, improve code style progressively
Common Style Issues
- Over-optimization: Don’t over-complicate code to comply with standards
- Tool Conflicts: Ensure different tool settings are compatible with each other
- Legacy Code: Consider impact scope when dealing with legacy code
- 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.