mcpField
P
python-best-practices

python-best-practices

github.com/jefflester开发4337 次安装6 天前更新
代码仓库

关于 python-best-practices

为 Python 开发提供代码质量与一致性指导的技能,适用于编写、重构和维护 Python 项目。内容涵盖 PEP 8、类型提示、文档字符串、错误处理与常见代码组织模式。

主要功能

  1. 01PEP 8 风格规范
  2. 02类型提示与现代语法
  3. 03NumPy 风格文档字符串
  4. 04异常处理与上下文管理器
  5. 05数据类、枚举与 pathlib
  6. 06pytest 测试实践

适用场景

  1. 01编写符合一致风格的 Python 代码
  2. 02为函数与类补充类型提示和文档
  3. 03重构文件与模块组织
  4. 04改进异常处理和文件操作

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jefflester/claude-skills-supercharged --skill python-best-practices

安装后在 Claude Code 中输入/python-best-practices确认已加载。

详细介绍

Python 最佳实践

目的

本技能提供 Python 开发最佳实践指南,帮助确保 Python 项目中的代码质量、可维护性和一致性。

何时使用此技能

在以下情况下自动激活:

  • 处理 Python 文件(*.py)
  • 提及“python”“best practices”“style guide”
  • 添加类型提示或文档字符串
  • 重构 Python 代码

风格指南

遵循 PEP 8

遵循 Python 代码的 PEP 8 风格指南:

  • 缩进:每级缩进 4 个空格
  • 行长度:代码最多 79 个字符,文档字符串/注释最多 72 个字符
  • 空行:顶层定义之间空 2 行,方法之间空 1 行
  • 导入:始终放在文件顶部,并按标准库、第三方库、本地模块分组
  • 命名约定:
    • 函数、变量和模块使用 snake_case
    • 类使用 PascalCase
    • 常量使用 UPPER_SNAKE_CASE
    • 内部/私有名称以下划线 _name 开头
导入组织

始终按以下顺序组织导入:

# 1. Standard library imports
import os
import sys
from pathlib import Path

# 2. Third-party imports
import requests
import numpy as np

# 3. Local application imports
from myapp.core import MyClass
from myapp.utils import helper_function

使用 TYPE_CHECKING 避免循环导入:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from myapp.other_module import OtherClass

def my_function(obj: "OtherClass") -> None:
    """Function that uses OtherClass only for type hints."""
    pass

类型提示

始终使用类型提示

类型提示有助于提高代码清晰度并及早发现错误:

def process_data(
    items: list[str],
    max_count: int | None = None,
    verbose: bool = False
) -> dict[str, int]:
    """Process items and return counts.

    Parameters
    ----------
    items : list[str]
        List of items to process
    max_count : int | None, optional
        Maximum items to process, by default None
    verbose : bool, optional
        Enable verbose output, by default False

    Returns
    -------
    dict[str, int]
        Dictionary mapping items to counts
    """
    result: dict[str, int] = {}

    for item in items[:max_count]:
        result[item] = result.get(item, 0) + 1
        if verbose:
            print(f"Processed: {item}")

    return result
现代类型语法(Python 3.10+)

使用现代联合类型语法 |,而不是 Union:

# Good (Python 3.10+)
def get_value(key: str) -> int | None:
    pass

# Avoid (old style)
from typing import Union, Optional
def get_value(key: str) -> Optional[int]:
    pass
泛型类型

使用内置泛型类型(Python 3.9+):

# Good (Python 3.9+)
def process_list(items: list[str]) -> dict[str, int]:
    pass

# Avoid (old style)
from typing import List, Dict
def process_list(items: List[str]) -> Dict[str, int]:
    pass

文档字符串

NumPy 风格文档字符串

使用 NumPy 风格的文档字符串,以保持一致性:

def calculate_statistics(
    data: list[float],
    include_median: bool = True
) -> dict[str, float]:
    """Calculate statistical measures for a dataset.

    This function computes mean, standard deviation, and optionally
    median for the provided dataset.

    Parameters
    ----------
    data : list[float]
        List of numerical values to analyze
    include_median : bool, optional
        Whether to calculate median, by default True

    Returns
    -------
    dict[str, float]
        Dictionary containing:
        - 'mean': arithmetic mean
        - 'std': standard deviation
        - 'median': median value (if include_median=True)

    Raises
    ------
    ValueError
        If data is empty or contains non-numeric values

    Examples
    --------
    >>> calculate_statistics([1.0, 2.0, 3.0, 4.0, 5.0])
    {'mean': 3.0, 'std': 1.414, 'median': 3.0}

    Notes
    -----
    Standard deviation uses Bessel's correction (ddof=1).
    """
    if not data:
        raise ValueError("Data cannot be empty")

    # Implementation here
    pass
类文档字符串
class DataProcessor:
    """Process and transform data from various sources.

    This class provides methods for loading, transforming, and
    validating data from multiple input formats.

    Parameters
    ----------
    source_dir : Path
        Directory containing source data files
    cache_enabled : bool, optional
        Enable result caching, by default True

    Attributes
    ----------
    source_dir : Path
        Directory path for source files
    cache : dict[str, Any]
        Cache for processed results

    Examples
    --------
    >>> processor = DataProcessor(Path("/data"))
    >>> results = processor.process_files()
    """

    def __init__(self, source_dir: Path, cache_enabled: bool = True):
        """Initialize the DataProcessor."""
        self.source_dir = source_dir
        self.cache: dict[str, Any] = {} if cache_enabled else None

错误处理

指定异常类型

使用指定的异常类型,不要使用裸 except:

# Good
try:
    with open(file_path) as f:
        data = f.read()
except FileNotFoundError:
    logger.error(f"File not found: {file_path}")
    raise
except PermissionError:
    logger.error(f"Permission denied: {file_path}")
    raise

# Avoid
try:
    with open(file_path) as f:
        data = f.read()
except:  # Too broad!
    pass
上下文管理器

始终使用上下文管理器管理资源:

# Good
with open(file_path) as f:
    content = f.read()

# Avoid
f = open(file_path)
content = f.read()
f.close()  # Easy to forget!
自定义异常

为特定领域的错误定义自定义异常:

class ValidationError(Exception):
    """Raised when data validation fails."""
    pass

class DataProcessingError(Exception):
    """Raised when data processing encounters an error."""

    def __init__(self, message: str, item_id: str):
        super().__init__(message)
        self.item_id = item_id

常见模式

使用数据类表示数据结构

对于简单的数据容器,使用 dataclasses:

from dataclasses import dataclass, field

@dataclass
class User:
    """User profile information."""

    username: str
    email: str
    age: int
    tags: list[str] = field(default_factory=list)
    is_active: bool = True

    def __post_init__(self):
        """Validate fields after initialization."""
        if self.age < 0:
            raise ValueError("Age cannot be negative")
使用枚举表示固定集合

使用 Enum 表示固定的值集合:

from enum import Enum, auto

class Status(Enum):
    """Processing status values."""

    PENDING = auto()
    PROCESSING = auto()
    COMPLETED = auto()
    FAILED = auto()

# Usage
current_status = Status.PENDING
if current_status == Status.COMPLETED:
    print("Done!")
使用 Pathlib 进行文件操作

使用 pathlib.Path,而不是 os.path:

from pathlib import Path

# Good
data_dir = Path("/data")
file_path = data_dir / "input.txt"

if file_path.exists():
    content = file_path.read_text()

# Avoid
import os
data_dir = "/data"
file_path = os.path.join(data_dir, "input.txt")

if os.path.exists(file_path):
    with open(file_path) as f:
        content = f.read()
列表推导式

使用推导式提升清晰度和性能:

# Good
squared = [x**2 for x in range(10) if x % 2 == 0]

# Avoid
squared = []
for x in range(10):
    if x % 2 == 0:
        squared.append(x**2)

代码组织

模块结构

将模块划分为清晰的部分:

"""Module for data processing utilities.

This module provides functions for loading, transforming, and
validating data from various sources.
"""

# Standard library imports
import os
import sys
from pathlib import Path

# Third-party imports
import requests
import pandas as pd

# Local imports
from myapp.core import BaseProcessor
from myapp.utils import validate_input

# Constants
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30

# Exceptions
class ProcessingError(Exception):
    """Raised when processing fails."""
    pass

# Functions
def load_data(source: str) -> pd.DataFrame:
    """Load data from source."""
    pass

# Classes
class DataProcessor(BaseProcessor):
    """Process and validate data."""
    pass

# Module initialization
if __name__ == "__main__":
    # CLI entry point
    main()
避免魔法数字

使用命名常量,而不是魔法数字:

# Good
MAX_RETRIES = 3
TIMEOUT_SECONDS = 30

def fetch_data(url: str) -> dict:
    for attempt in range(MAX_RETRIES):
        response = requests.get(url, timeout=TIMEOUT_SECONDS)
        if response.status_code == 200:
            return response.json()

# Avoid
def fetch_data(url: str) -> dict:
    for attempt in range(3):  # What is 3?
        response = requests.get(url, timeout=30)  # Why 30?
        if response.status_code == 200:
            return response.json()

测试

使用 pytest 进行测试
import pytest
from myapp.processor import DataProcessor

def test_process_valid_data():
    """Test processing with valid input."""
    processor = DataProcessor()
    result = processor.process([1, 2, 3])
    assert result == [2, 4, 6]

def test_process_empty_data():
    """Test processing with empty input."""
    processor = DataProcessor()
    with pytest.raises(ValueError):
        processor.process([])

@pytest.fixture
def sample_data():
    """Provide sample data for tests."""
    return [1, 2, 3, 4, 5]

def test_with_fixture(sample_data):
    """Test using fixture."""
    processor = DataProcessor()
    result = processor.process(sample_data)
    assert len(result) == len(sample_data)

要点总结

  1. 始终遵循 PEP 8 风格指南
  2. 始终为函数签名添加类型提示
  3. 为所有公共函数/类编写 NumPy 风格的文档字符串
  4. 使用指定的异常类型,不要使用裸 except
  5. 优先使用 pathlib.Path,而不是 os.path
  6. 使用数据类和枚举表示结构化数据
  7. 按标准库 → 第三方库 → 本地模块的顺序组织导入
  8. 避免使用魔法数字,改用命名常量
  9. 使用 pytest 编写测试
  10. 使用现代 Python 语法(3.9+)

评论

支持 Markdown