Python代码规范

目录

1.代码风格

1.1 行长度

1.2 空行

1.3 库/模块导入顺序:

1.4 缩进

1.5 模块公共接口控制

4.1 函数参数

4.1.1 函数参数顺序

4.1.2 函数参数个数

4.1.3 函数参数类型提示

4.2 文档字符串(docstring)

4.3 lru_cache装饰器

5.1 文档字符串(docstring)

5.2 类的定义方式

5.2.1 “半结构化类”或 “手动初始化数据类”

5.2.2 使用dataclass进行类的修饰定义

5.3 类的成员变量设计

5.4 类的成员方法设计

7.1 模块

7.2 包

7.3 包的导入方式

7.3.1 相对导入

7.3.2 动态导入

8.1 Path库


        Python编程规范绝非对开发者个性的束缚,恰恰相反,它是保障代码质量、提升协作效率和维系项目生命力的基石。其核心重要性体现在以下几个方面:

        首先,它极大地提升了代码的可读性与可维护性。 Python哲学强调“代码是写给人看的,只是顺带让机器执行”。统一的命名、一致的缩进、清晰的结构使得代码如同一本排版精美的书,任何开发者都能迅速理解其意图,而非在混乱的符号中艰难解密。这在人员更替或长期维护时至关重要,能显著降低“技术债务”和心智负担。

        其次,它是团队协作的“通用语言”和“润滑剂”。 在多人参与的项目中,若没有统一的规范,每位成员的编码风格都会为项目注入不确定性,导致沟通成本急剧上升,代码评审沦为格式纠错的拉锯战。而遵循PEP 8等公认规范,相当于所有成员遵守同一份蓝图,使得协作焦点可以集中于逻辑与架构等更高层次的问题,而非琐碎的格式分歧,从而大幅提升团队整体效能。

        再者,它是软件质量与可靠性的前置保障。 许多规范条目,如明确的异常处理、合理的函数长度、避免魔法数字等,本身就是长期实践总结出的最佳实践。它们能有效规避常见陷阱,减少隐藏的Bug,引导开发者写出更健壮、更安全的代码。配合pylintflake8black等自动化工具,规范检查能被集成到开发流程中,在问题发生前就予以纠正。

        最后,它体现了专业工程师的素养与职业精神。 编写符合规范的代码,是对同事、对项目、也是对自己职业的尊重。它展现了一种严谨、负责的态度,是个人技术品牌的重要组成部分,也是高级工程师与初学者的关键区别之一。

        总而言之,投资于学习并践行编程规范,短期看是规则的适应,长期看则是一项回报极高的投资。它所带来的代码一致性、可维护性和团队协作效率的提升,是任何一个希望持续发展、规模化的软件项目所不可或缺的底层支撑。

        根据小编在工作中的项目实践经验,以及网上各位前辈的归纳总结,整理了一份Python编程规范供大家参考使用。(规范内容如有不合理之处,请各位不吝赐教,大家共同进步~)

1.代码风格

1.1 行长度

        ≤120字符(注释/字符串除外)

1.2 空行

        模块级函数/类之间:2行

        类内方法之间:1行

1.3 /模块导入顺序:

        标准库 -> 第三方库 -> 本地模块

# 示例
import os
import sys

import numpy as np
import pandas as pd

from . import utils
from .config import settings

        模块导入规范:

1.3.1 禁止使用 import * 

        污染命名空间:导入大量不需要的名称

        可读性降低:无法追踪名称的来源

        命名冲突:不同模块的同名对象会互相覆盖

        破坏 IDE 的智能提示和静态分析

# 禁止
from module import *

# 推荐
import module
from module import specific_function, SpecificClass

1.3.2 禁止隐式相对导入

        污染命名空间:导入大量不需要的名称

        可读性降低:无法追踪名称的来源

# 禁止 (Python 2 风格)
import sibling_module

# 推荐 - 绝对导入
from package import module
from . import sibling_module  # 显式相对导入

1.4 缩进

        使用4个空格(禁用Tab)

1.5 模块公共接口控制

        使用__all__模块变量当前py文件公共接口进行管控明确声明模块的公共 API声明模块存在当前文件调用无法提供外部

备注:私有函数命名用_开头

import inspect

# 自动导出所有不以 _ 开头的函数
__all__ = [name for name, obj in inspect.getmembers(sys.modules[__name__])
           if inspect.isfunction(obj) and not name.startswith('_')]
           
# 导出指定的函数
__all__ = ['module1', 'module2']

2. 命名规范

        驼峰命名法‌:使用大小写字母区分单词,常用于类名、枚举、特征等类型定义

‌        蛇形命名法‌:使用下划线分隔单词, 适用于变量、函数、模块名等值类型定义

命名方法

示例

类名

大驼峰法(所有单词首字母大写)

DataBaseUser

变量名

蛇形命名小写单词通过_串联最后单词变量类型list,dictcls

count_list

函数

蛇形命名全小写,单词通过_串联尽量动词开头

get_valid_data

常量

蛇形命名全部大写

DATA_TYPE_MAP

私有成员

蛇形命名,第一个字符为_,且所有字符均小写

_internal_cache

3 函数设计

        单一职责原则:每个函数只做一件事

        最小惊奇原则:函数行为应直观可预测

        开闭原则:对扩展开放,对修改关闭

3.1 函数参数

3.1.1 函数参数顺序

# 推荐顺序

def process_data(

required_arg, # 必需位置参数

*args, # 可变位置参数,

*, # 如果存在则表示*前面的参数为位置参数(也可以指定为关键字参数),*后面的参数必 须显式地使用参数名来传递

optional_arg=None, # 可选关键字参数

**kwargs # 可变关键字参数

) -> ResultType:
    pass

4.1.2 函数参数个数

        理想参数个数0-3最多不超过5超过考虑使用对象参数

4.1.3 函数参数类型提示

        所有参数指定类型提示包括返回

        多返回值的情况

# (1)使用命名元组
from collections import namedtuple

Result = namedtuple('Result', ['success', 'data', 'message'])

def processData() -> Result:
    ...
    return Result(success=True, data=processed, message="OK")


# (2)自定义类型ServiceLengthDict = Dict[ServiceName, Dict[OperationName, LengthValue]]
UserDefine = Tuple[str, Dict[str, Union[Dict[str, str], LengthValue]], float]

4.2 文档字符串(docstring)

        统一采用Google 风格文档,模板格式:

"""函数注释

Attributes:

参数 (变量类型): 参数说明



Raises:

异常类型(继承Exception类或其子类): 异常类型说明

Return:

返回值类型:返回值说明

Examples:

>>> instance = 函数名(入参)

返回值

"""

# 示例
def byte_order(data: int, msb_index: int, data_len: int, byte_order_type: str = "motorola") -> list[int]:
    """
    Attributes:
        data(int): data to convert
        msb_index(int): bit index of MSB
        data_len(int): data length
        byte_order_type(str): motorola or big_endian / intel or little_endian

    Returns:
        big_endian list
    
    Examples:
    >>> byte_order_result = byte_order(data=12, msb_index=14, data_len=4, byte_order_type="motorola")
    """
    ...

4.3 lru_cache装饰器

        使用@lru_cache(maxsize=None)装饰器,会自动缓存函数的计算结果,当使用相同的参数再次调用时直接返回缓存结果,避免重复计算。

5 类设计

5.1 文档字符串(docstring)

        统一采用Google 风格文档,模板格式:

class DocumentStringDemo:
    """一个演示 Google 风格文档字符串用法的类。

    该类展示了如何为类和方法编写规范的文档字符串,
    包括参数说明、返回值说明和示例代码等。

    Attributes:
        name (str): 实例名称,用于标识对象。
        value (int): 实例值,用于存储数值数据。
    """

    def __init__(self, name, value):
        """初始化实例。

        Args:
            name (str): 实例名称,长度不超过20个字符。
            value (int): 实例值,必须为正整数。

        Raises:
            ValueError: 如果value不是正整数时抛出。
        """
        if not isinstance(value, int) or value <= 0:
            raise ValueError("value必须是正整数")
        self.name = name
        self.value = value

    def calculate(self, factor):
        """基于实例值和输入因子计算结果。

        该方法将实例值与输入因子相乘,返回计算结果。

        Args:
            factor (float): 计算因子,可以是任意浮点数。

        Returns:
            float: 计算结果,保留2位小数。

        Examples:
            >>> demo = DocumentStringDemo("test", 10)
            >>> demo.calculate(1.5)
            15.0
        """
        return round(self.value * factor, 2)

    def get_info(self):
        """获取实例的描述信息。

        Returns:
            str: 包含实例名称和值的格式化字符串。
        """
        return f"Name: {self.name}, Value: {self.value}"
 

5.2 定义方式

5.2.1 “半结构化类”或 “手动初始化数据类”

        一种介于传统 Python 类和现代数据类之间的混合设计模式,通过__init__手动实现构造器,完成类成员属性初始化操作

        所有的初始参数通过__init__传入完成初始化构造

5.2.2 使用dataclass进行类的修饰定义

        自动构造器生成,无需手动编写 __init__,直接定义成员变量包括初始化成员禁止初始成员通过dataclasses.field(init=False)禁止

        使用def __post_init__(self) 进行后初始化操作,在类的实例被初始化之后立即调用的一个特殊方法。它允许在实例创建之后执行一些额外的初始化逻辑。这对于需要在基本初始化之后进行复杂计算或设置的情况非常有用,场景

(1)禁止初始化类型成员过程进行逻辑赋值

2规则校验实例化对象是否满足基础规则验证属性有效性

Python dataclasses 详解:简化类定义与高级技巧-CSDN博客

5.3 类的成员变量设计

5.3.1 命名

        公有成员:直接使用snake_case命名,无前缀。应通过属性(property)或方法控制访问,避免直接暴露内部状态。

        受保护成员:单下划线前缀(_snake_case),表示仅供子类或内部使用,外部不应访问。

        私有成员:双下划线前缀(__snake_case),会触发名称改写(name mangling),避免子类意外覆盖。

class DataProcessor:
    # 公有变量 (外部可直接访问)
    api_endpoint: str
    
    # 受保护变量 (子类可访问,约定不直接外部访问)
    _cache_size: int
    
    # 私有变量 (仅类内访问,会触发名称改写)
    __internal_counter: int = 0
    
    # 常量 (全大写命名)
    MAX_RETRIES: ClassVar[int] = 3

5.3.2 类型注解

        所有成员变量必须使用类型注解(如: str, int, List[Dict]等)。

        复杂类型使用typing模块(如Optional, Union, Tuple等)。

from typing import Optional, Union, TypedDict

class ConnectionConfig(TypedDict):
    host: str
    port: int

class NetworkClient:
    # 基本类型
    timeout: float
    
    # 复合类型
    endpoints: list[ConnectionConfig]
    
    # 可选类型
    fallback_server: Optional[ConnectionConfig] = None
    
    # 联合类型
    credentials: Union[str, tuple[str, str]]
    
    # 泛型集合
    error_counts: dict[str, int] = field(default_factory=dict)

5.3.3 默认值设置

        简单默认值:直接在类型注解后赋值,如 count: int = 0。

        可变默认值:使用dataclasses.field的default_factory,避免可变对象的共享问题。

例如:items: list[str] = field(default_factory=list)

from dataclasses import field

class UserProfile:
    # 简单默认值
    is_verified: bool = False
    
    # 避免可变默认值陷阱
    access_history: list[datetime] = field(default_factory=list)
    
    # 延迟初始化
    _avatar_data: bytes = field(init=False, repr=False)
    
    def __post_init__(self):
        self._avatar_data = self._load_default_avatar()

5.3.4 常量

        使用全大写SCREAMING_SNAKE_CASE,并在类内直接定义。

        类型注解可选,但推荐加上,如:MAX_SIZE: ClassVar[int] = 1024

5.3.5 属性(property)

        用于封装成员变量的访问,添加逻辑验证或计算属性。

        只读属性:使用@property装饰器,不提供setter。

        读写属性:提供setter,并在setter中进行验证。

class TemperatureSensor:
    # 私有变量
    _current_temp: float
    
    @property
    def temperature(self) -> float:
        """当前温度(只读)"""
        return self._current_temp
    
    @property
    def status(self) -> str:
        """设备状态(计算属性)"""
        return "ONLINE" if self._is_active else "OFFLINE"
    
    @temperature.setter
    def temperature(self, value: float):
        """温度设置器(带验证)"""
        if not -40 <= value <= 150:
            raise ValueError("温度超出有效范围")
        self._current_temp = value
 
 

5.3.6文档字符串

        每个公有成员和受保护成员应在类文档字符串的Attributes部分说明。

        私有成员可在内部注释中说明。

5.4 类的成员方法设计

5.4.1 参数和返回值

        所有参数和返回值必须使用类型注解

        参数命名:snake_case,避免单个字符(除非是数学中的常规用法,如x,y)。

        默认参数:不可变类型可直接设置默认值,可变类型使用None,并在函数内初始化。

5.4.2 文档字符串(Google风格)

        简要说明:单行概述功能。

        详细说明:可选,多行描述,Google风格文档

5.4.3 方法设计原则

        单一职责:一个方法只做一件事。

        方法长度:尽量不超过50行,过长应考虑拆分。

5.4.4 静态方法和类方法

        静态方法(@staticmethod):与类相关但不需要访问实例或类状态的方法。通常用于工具函数。

        类方法(@classmethod):用于操作类状态(类变量)或创建替代构造函数。

5.4.5 私有方法

        仅在类内部使用,外部不应调用。

        命名以单下划线_开头,但不会触发名称改写。

        可在文档字符串中简单说明,但不必在类文档中公开。

6 存根文件(.pyi)

        存根文件(.pyi) 是Python用于定义接口类型但不包含具体实现的特殊文件。它提供了一种独立于实现的类型定义方式,核心特点:

  • 纯接口声明:只包含函数签名、类结构和变量类型注释
  • 运行时忽略:Python解释器不会加载执行.pyi文件
  • 类型检查器专用:供mypy、pyright等工具执行类型检查
  • 三斜杠占位:使用...替代具体实现代码

Python 存根文件(.pyi)详解:类型提示的高级指南-CSDN博客

以socket类为例:

class socket(_socket.socket):
    def __init__(
        self, family: AddressFamily | int = -1, type: SocketKind | int = -1, proto: int = -1, fileno: int | None = None
    ) -> None: ...
    def __enter__(self) -> Self: ...
    def __exit__(self, *args: Unused) -> None: ...
    def dup(self) -> Self: ...  # noqa: F811
    def accept(self) -> tuple[socket, _RetAddress]: ...
    # Note that the makefile's documented windows-specific behavior is not represented
    # mode strings with duplicates are intentionally excluded
    @overload
    def makefile(
        self,
        mode: Literal["b", "rb", "br", "wb", "bw", "rwb", "rbw", "wrb", "wbr", "brw", "bwr"],
        buffering: Literal[0],
        *,
        encoding: str | None = None,
        errors: str | None = None,
        newline: str | None = None,
    ) -> SocketIO: ...
    @overload
    def makefile(
        self,
        mode: Literal["rwb", "rbw", "wrb", "wbr", "brw", "bwr"],
        buffering: Literal[-1, 1] | None = None,
        *,
        encoding: str | None = None,
        errors: str | None = None,
        newline: str | None = None,
    ) -> BufferedRWPair: ...
    @overload
    def makefile(
        self,
        mode: Literal["rb", "br"],
        buffering: Literal[-1, 1] | None = None,
        *,
        encoding: str | None = None,
        errors: str | None = None,
        newline: str | None = None,
    ) -> BufferedReader: ...
    @overload
    def makefile(
        self,
        mode: Literal["wb", "bw"],
        buffering: Literal[-1, 1] | None = None,
        *,
        encoding: str | None = None,
        errors: str | None = None,
        newline: str | None = None,
    ) -> BufferedWriter: ...
    @overload
    def makefile(
        self,
        mode: Literal["b", "rb", "br", "wb", "bw", "rwb", "rbw", "wrb", "wbr", "brw", "bwr"],
        buffering: int,
        *,
        encoding: str | None = None,
        errors: str | None = None,
        newline: str | None = None,
    ) -> IOBase: ...
    @overload
    def makefile(
        self,
        mode: Literal["r", "w", "rw", "wr", ""] = "r",
        buffering: int | None = None,
        *,
        encoding: str | None = None,
        errors: str | None = None,
        newline: str | None = None,
    ) -> TextIOWrapper: ...
    def sendfile(self, file: _SendableFile, offset: int = 0, count: int | None = None) -> int: ...
    @property
    def family(self) -> AddressFamily: ...
    @property
    def type(self) -> SocketKind: ...
    def get_inheritable(self) -> bool: ...
    def set_inheritable(self, inheritable: bool) -> None: ...

7 模块与包

7.1 模块

一个 .py 文件就是一个模块

模块名就是文件名(不含 .py 后缀)

模块可以包含函数、类、变量和可执行代码

7.2

包含 __init__.py 文件的目录

可以包含多个模块和子包

用于组织相关模块的层次结构

结构

my_package/
├── __init__.py   # 可以理解为包的标志性文件,一个文件夹下有此文件,即此文件夹即可以作为一个包被导入
├── module_a.py
├── module_b.py
└── sub_package/
    ├── __init__.py
    └── module_c.py

7.3导入方式

7.3.1 相对导入

        相对导入只能在包内使用(即包含__init__.py的目录)

        假设有一个项目结构如下:

my_project/
    ├── package/
    │   ├── __init__.py
    │   ├── module_a.py
    │   ├── module_b.py
    │   └── subpackage/
    │       ├── __init__.py
    │       └── module_c.py
    └── script.py
 

        在module_a.py中导入同级的module_b.py

from . import module_b

        在module_a.py中导入子包subpackage中的module_c.py

from .subpackage import module_c

        在subpackage/module_c.py中导入父级目录的module_a.py

from .. import module_a

7.3.2 动态导入

from importlib import import_module

_NAME_TO_MODULE = {
        # Com_Arxml_Extraction 模块的属性
        "func1": ".Module",
        "func2": ".Module",
        "func3": ".Module",
        "func4": ".Module",
        "func5": ".Module"
    }


__all__ = list(_NAME_TO_MODULE.keys())


def __getattr__(name: str):
    if name not in _NAME_TO_MODULE:
        raise AttributeError(f"模块 {__name__} 没有属性 {name}")

    try:
        module = import_module(_NAME_TO_MODULE[name], package=__package__)
    except ImportError as e:
        raise ImportError(f"无法导入子模块 {_NAME_TO_MODULE[name]},请检查路径或依赖:{e}") from e

    try:
        return getattr(module, name)
    except AttributeError as e:
        raise AttributeError(f"子模块 {module.__name__} 中未找到属性 {name}") from e

8 Path

涉及文件系统路径操作尽可能使用Path

Python中的pathlib和Path(面向对象的文件系统路径操作库)-CSDN博客

    Python代码规范通常遵循PEP 8(Python Enhancement Proposal 8)标准,下面是一些常见的Python代码规范: 1. 缩进:使用4个空格进行缩进,不要使用制表符。 2. 行长度:每行代码应尽量控制在79个字符以内,可以使用括号进行换行。 3. 空行:在函数和类定义之间、函数内的逻辑块之间使用空行进行分隔,以提高可读性。 4. 导入语句:每个导入语句应独占一行,按照标准库、第三方库和本地库的顺序进行分组。 5. 命名规范:变量名、函数名和模块名应使用小写字母,单词之间使用下划线进行分隔。类名应使用驼峰命名法。 6. 注释:使用注释来解释代码的功能、实现思路等。注释应该清晰、简洁,并且避免使用无意义的注释。 7. 函数和方法:函数和方法的命名应该清晰、简洁,并且能够准确描述其功能。函数和方法的参数应该有明确的命名,并且避免使用单个字符作为参数名。 8. 类:类的命名应该使用驼峰命名法,并且首字母大写。类应该有一个简洁明确的目的,并且遵循单一职责原则。 9. 异常处理:在可能发生异常的地方进行适当的异常处理,并且避免使用裸露的except语句。 10. 其他规范:避免使用全局变量,尽量使用局部变量;避免使用魔术数字,使用常量代替;避免使用复杂的表达式,尽量拆分为多个简单的表达式。 以上是一些常见的Python代码规范,遵循这些规范可以提高代码的可读性和可维护性。如果你还有其他问题,请继续提问。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值