器→工具, 编程语言

Python 编码规范整理版

钱魏Way · · 306 次浏览
!文章内容如有错误或排版问题,请提交反馈,非常感谢!

以下是根据规则修复空格后的内容:

“`html
以下是一份结合PEP8规范、最佳实践及常见注意事项的Python编码规范整理,适用于团队协作与个人项目:

代码布局与格式

缩进

  • 规则:使用4个空格(禁止使用Tab键)。
  • 多行缩进:垂直对齐或悬挂缩进(后续行多缩进一级)。
# 正确:与括号对齐
result = some_function(arg1, arg2,
arg3, arg4)
# 正确:悬挂缩进(多一层缩进)
result = some_function(
arg1, arg2,
arg3, arg4
)

在Python编码规范中,垂直对齐和悬挂缩进是两种处理代码换行的缩进方式,目的是让多行代码更清晰易读。

垂直对齐(Vertical Alignment)

  • 定义:当一行代码过长需要换行时,续行的代码与包裹元素的起始位置对齐(例如括号、方括号、花括号等)。这种对齐方式通过视觉上的整齐排列,强调代码的结构层级。
  • 适用场景:函数调用、列表/字典/元组定义、条件判断等需要多行表达的场景。
  • 优点:代码结构直观,容易看出参数或元素的层级关系。
  • 缺点:当包裹元素(如括号)位置较深时,可能导致续行代码缩进过多,占用行宽。

悬挂缩进(Hanging Indent)

  • 定义:当一行代码过长需要换行时,续行的代码比父级代码多一层缩进(通常为4个空格)。首行不放置任何元素,续行代码从下一行开始。
  • 适用场景:函数定义/调用、字典/列表等需要换行且父级代码较长的情况。
  • 优点:避免首行过长,代码层次分明,适合复杂结构。更符合PEP8对行长度的限制(79字符)。
  • 缺点:首行可能显得空荡,需要适应这种风格。

PEP8的推荐

  • – 优先使用悬挂缩进:PEP8推荐在函数定义、调用等场景中使用悬挂缩进,因为它更符合行长度限制,且易于工具自动格式化。
  • – 垂直对齐的例外:当代码逻辑需要强调对齐关系时(如字典键值对),垂直对齐更直观。

行长度

  • 规则:每行不超过79字符(PEP8推荐),可放宽至88-100字符(现代IDE支持)。
  • 换行策略:
    • 在运算符前换行。
    • 使用反斜杠\ 或括号包裹换行。
# 正确:运算符后换行
total = (value1 + value2
+ value3 - value4)

空格与运算符

  • 运算符两侧加空格,但括号内不需要:
# 正确
x = 1 + 2
if a > 5 and b< 10:
# 错误
x=1+2
  • 逗号、分号后加空格,前面不加:
# 正确
arr = [1, 2, 3]
# 错误
arr=[1,2,3]

空行

  • 函数/类之间:用2个空行分隔。
  • 函数内部:用1个空行分隔逻辑块。
def func1():
pass


def func2():
pass

导入(Imports)

  • 模块导入顺序:标准库→第三方库→本地模块,每组用空行分隔。
  • 禁止通配符:禁止使用from module import *。
import os
import sys

from django.http import HttpResponse
from myapp.utils import helper

命名规范

基本规则

  • 变量/函数:小写+下划线(snake_case),如calculate_total。
  • 类名:首字母大写(CamelCase),如ClassName。
  • 常量:全大写+下划线,如MAX_LENGTH。
  • 私有成员:前缀单下划线_private_var,双下划线 __mangled_name(名称修饰)。

避免的命名

  • 单字母变量(除非在循环或lambda中)。
  • 保留关键字(如list、str)或易混淆名称(如 l, O)。

代码风格

表达式与语句

避免无关空格

# 错误
func(arg1, arg2 )
# 正确
func(arg1, arg2)
  • 链式比较:允许if 0< x< 10。
  • 条件简写:避免冗余代码。
# 冗余
if x == True:
# 正确
if x:

函数与方法

  • 函数长度不超过50行,功能单一(一个函数只做一件事)。
  • 参数默认值:避免可变对象(如列表、字典)。
# 错误
def func(a=[]):
# 正确
def func(a=None):
a = a or []

在Python中,函数参数的默认值如果是可变对象(如列表、字典等),可能会导致意外的副作用。这是因为默认值在函数定义时就被计算并存储,后续调用会共享同一个可变对象。以下是详细解释和示例:

问题的根源:Python的默认参数值是在函数定义时就被计算并存储的,而不是每次调用时重新创建。如果默认值是可变对象(如`[]`或`{}`),所有未显式传递该参数的调用都会共享同一个对象。

示例代码:

def append_to_list(value, lst=[]):
lst.append(value)
return lst

print(append_to_list(1)) # 输出[1]
print(append_to_list(2)) # 输出[1,2]❌预期是[2]

为什么会出现问题?

  • 第一次调用`append_to_list(1)`时,`lst`使用默认的空列表`[]`,结果变为`[1]`。
  • ```第二次调用`append_to_list(2)`时,`lst`仍然指向第一次调用时修改后的列表`[1]`,所以结果变为`[1,2]`。

如何避免?

正确的做法是将默认值设为不可变对象(如`None`),然后在函数内部判断并初始化可变对象:

修正代码:

def append_to_list(value, lst=None):
    if lst is None:
        lst = []  # 每次调用时创建新列表
    lst.append(value)
    return lst

print(append_to_list(1))  # 输出 [1]
print(append_to_list(2))  # 输出 [2] ✅

类型注解:推荐使用类型提示(Python 3.5+)。

def greet(name: str) -> str:
    return f"Hello, {name}"

类设计

  • 类的职责明确,避免“上帝类”。
  • 使用`@property`装饰器管理属性访问。
  • 优先使用组合而非继承。

单一职责原则(SRP)

  • 定义:一个类应该只有一个引起它变化的原因(即一个类只做一件事)。
  • 核心思想:将功能拆分为独立的模块,每个模块负责一个明确的职责。

示例对比:

# 错误设计:一个类同时处理用户认证、数据存储和日志记录。
# 上帝类的典型表现
class UserManager:
    def login(self, username, password): ...  # 认证
    def save_to_db(self, user_data): ...  # 数据存储
    def write_log(self, message): ...  # 日志记录

# 正确设计:职责拆分到不同类。
class UserAuthenticator:
    def login(self, username, password): ...  # 只负责认证

class UserRepository:
    def save(self, user_data): ...  # 只负责存储

class Logger:
    def log(self, message): ...  # 只负责日志

如何判断职责是否明确?

  • 类的名称是否清晰描述其功能?(如`UserAuthenticator`而非`UserManager`)
  • 能否用一句话描述类的职责?(例如:“这个类负责验证用户身份”)
  • 修改某个功能时,是否需要改动多个类?

什么是“上帝类”(God Class)?

定义

  • 上帝类:一个类承担了太多职责,包含大量方法和属性,甚至直接操作其他类的数据。
  • 典型特征:
    • 代码量庞大(数千行)。
    • 直接依赖多个外部模块或数据。
    • 包含大量不相关的方法(如同时处理网络请求、数据库操作、业务逻辑)。

上帝类的危害:

  • 代码耦合度高:牵一发而动全身,修改一处可能引发多处错误。
  • 难以测试:依赖复杂,单元测试难以覆盖所有场景。
  • 可读性差:新成员需要花费大量时间理解类的功能。

如何避免上帝类?

按功能拆分:将大类分解为多个小类,每个类负责单一功能。

class OrderCreator: ...  # 创建订单
class InventoryUpdater: ...  # 更新库存
class PaymentProcessor: ...  # 处理支付
class NotificationSender: ...  # 发送通知

使用组合而非继承:

  • 继承:容易导致父类膨胀(如`Animal`类包含所有动物的方法)。
  • 组合:通过注入依赖对象,灵活扩展功能。
# 错误:继承导致冗余
class Bird(Animal):
    def fly(self): ...  # 鸟类会飞
    def swim(self): ...  # 但有些鸟不会游泳

# 正确:通过组合分离能力
class Bird:
    def __init__(self, fly_behavior, swim_behavior):
        self.fly = fly_behavior
        self.swim = swim_behavior

依赖注入(Dependency Injection):将依赖的外部服务(如数据库、网络)通过构造函数或方法参数传入,而非在类内部直接创建。

# 错误:类内部直接依赖数据库
class UserRepository:
    def __init__(self):
        self.db = MySQLClient()  # 紧耦合


# 正确:通过依赖注入解耦
class UserRepository:
    def __init__(self, db_client):  # 可传入任意数据库客户端
        self.db = db_client

异常处理

基本原则

  • 避免捕获所有异常(`except:`❌),明确指定异常类型。
  • 异常处理用于预期可能出错的代码,而非控制流程。
  • 自定义异常应继承`Exception`基类。

明确异常类型:避免裸 except:。

# 错误
try:
    ...
except:
    pass

# 正确
try:
    ...
except ValueError as e:
    logger.error(e)

异常消息:提供清晰错误信息。

raise ValueError("Invalid value: expected int, got str")

注释与文档

代码注释

  • 行内注释:在代码后空两格写`#`,注释内容简明。
  • 块注释:用于复杂逻辑解释,每行以`#`开头。
  • 避免无意义的注释(如`# 赋值给 x`)。

文档字符串(Docstring)

  • 模块/函数/类:使用三重双引号"""...""",遵循 Google 或 NumPy 风格。
def calculate_sum(a: int, b: int) -> int:
    """Add two numbers.

    Args:
        a (int): First number.
        b (int): Second number.

    Returns:
        int: Sum of a and b.
    """
    return a + b

Google 风格文档字符串

特点:

  • 简洁直观:语法简单,强调自然语言描述。
  • 结构化标签:用`Args`、`Returns`、`Raises`等标签分块。
  • 类型标注可选:参数类型可写在描述中,或与参数名结合。
  • 广泛适用:适合中小型项目或快速编写文档。

格式示例:

def add(a: int, b: int) -> int:
"""计算两个整数的和。

Args:
    a (int): 第一个加数。
    b (int): 第二个加数。

Returns:
    int: 两个数的和。

Raises:
    ValueError: 如果输入非整数。

Examples:
    >>> add(2, 3)
    5
"""
if not isinstance(a, int) or not isinstance(b, int):
    raise ValueError("输入必须是整数")
return a + b

核心标签:

  • `Args`: 参数说明(可标注类型)。
  • `Returns`: 返回值说明。
  • `Raises`: 可能抛出的异常。
  • `Examples`: 使用示例(可包含 Doctest)。

NumPy 风格文档字符串

特点:

  • 详细规范:严格分块,适合复杂函数或库。
  • 类型强制标注:参数和返回值必须明确类型。
  • 多级分隔线:用 `----------` 分隔不同区块。
  • 科学计算偏好:常见于数据科学和数值计算项目(如 NumPy、Pandas)。

格式示例

def divide(dividend: float, divisor: float) -> float:
"""计算两个数的除法。

Parameters
----------
dividend: float
    被除数。
divisor: float
    除数,必须非零。

Returns
-------
float
    除法结果。

Raises
------
ZeroDivisionError
    如果除数为零。

Examples
--------
>>> divide(10.0, 2.0)
5.0
"""
if divisor == 0:
    raise ZeroDivisionError("除数不能为零")
return dividend / divisor

核心标签:

  • `Parameters`: 参数说明(强制类型标注)。
  • `Returns`: 返回值说明。
  • `Raises`: 异常说明。
  • `SeeAlso`: 相关函数或类。
  • `Notes`: 额外注意事项。
  • `Examples`: 使用示例。

TODO 注释

  • 标记待完成事项。
# TODO: Implement error handling here.

最佳实践与注意事项

代码可读性

  • 避免魔法代码:明确优于隐晦。
# 错误:魔法数字
if status == 2:
# 正确:使用常量
STATUS_COMPLETED = 2
if status == STATUS_COMPLETED:
  • 单一职责原则:一个函数只做一件事。

性能优化

避免低效操作

  • 字符串拼接使用 `join` 而非 `+`。
  • 优先用列表推导式代替 `for` 循环生成列表。
  • 减少全局变量访问(局部变量更快)。

高效数据结构

  • 频繁查找用 `字典` 或 `集合`(O(1) 复杂度)。
  • 大数据处理优先考虑生成器(`yield`)。

兼容性与版本

  • Python 2/3 兼容:明确代码运行环境(Python 2 已停止维护)。
  • 环境依赖:使用 txt 或 pyproject.toml 管理包。

工具推荐

  • 代码检查
    • flake8:综合检查(PEP8 + 代码复杂度)。
    • pylint:更严格的静态分析。
  • 自动格式化
    • black:无配置强制统一格式。
    • isort:自动排序导入语句。
  • 类型检查
    • mypy:静态类型检查。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注