← 博客 · · ConvertCaseHQ 团队 · 阅读约需 7 分钟

Python PEP 8 命名规范:变量和函数用 snake_case

PEP 8 规定,函数名和变量名应使用小写字母,为了提高可读性,需要时用下划线分隔单词。这就是 snake_case:total_price、send_invoice()。类使用 CapWords(InvoiceSender),常量使用带下划线的大写字母(MAX_RETRIES),模块则使用简短的全小写名称。你要写的大部分 Python 代码,有这几条就够了。

PEP 8 命名部分的其余内容,才是有用细节所在:前导和尾随下划线是什么意思,缩写怎么处理,哪些单个字母要避免,以及什么时候打破规则才是正确的做法。下面的内容都对照 PEP 8 原文核对过,凡是放在引号里的英文,都是 PEP 8 的原话。

PEP 8 命名规范一览

命名对象PEP 8 风格示例
模块简短,全小写;有助于阅读时可以用下划线billing.py、tax_rules.py
包简短,全小写;不建议用下划线payments
类CapWordsInvoiceSender
异常CapWords;如果是错误,以 Error 结尾PaymentDeclinedError
函数、方法snake_casesend_invoice()
变量、实例变量snake_casetotal_price
方法的第一个参数self;类方法用 clsdef from_settings(cls, settings)
常量大写字母加下划线,通常放在模块级别MAX_RETRIES
类型变量简短的 CapWordsT、AnyStr
非公开名称一个前导下划线_rate_cache

下面用一个小模块演示表中的大部分内容:

# billing.py
MAX_RETRIES = 3
_rate_cache = {}


class PaymentDeclinedError(Exception):
    pass


class InvoiceSender:
    def __init__(self, smtp_host):
        self.smtp_host = smtp_host
        self._attempts = 0

    def send_invoice(self, invoice_id):
        self._attempts += 1
        ...

    @classmethod
    def from_settings(cls, settings):
        return cls(settings["smtp_host"])

还有两处不太常见的内容值得了解。类型变量可以加 _co 或 _contra 后缀,表示协变或逆变,如 VT_co = TypeVar('VT_co', covariant=True)。另外,全局变量遵循与函数相同的规则;如果一个模块打算以 from module import * 的方式使用,PEP 8 建议把它的公开名称列在 __all__ 中。

为什么要用 snake_case?

PEP 8 诞生于 2001 年 7 月,列出的作者是 Guido van Rossum、Barry Warsaw 和 Alyssa Coghlan。它的出发点是 Guido 的一个观察:代码被阅读的次数远多于被编写的次数。命名规则就是为这个目标服务的。下划线让单词边界即使在很长的名称(days_since_last_login)里也清晰可见;snake_case 的值与 CapWords 的类之间的区别,则让你在去查一个名称之前,就能知道它是什么类型的东西。对于公共 API,PEP 8 还确立了一条压倒一切的原则:名称应当反映它是如何被使用的,而不是如何被实现的。

下划线的含义

Python 没有 private 关键字,所以下划线按惯例承担了这项工作。PEP 8 认可四种形式:

  • _single_leading:一个较弱的“internal use”(仅供内部使用)信号。from module import * 会跳过这些名称,但除此之外,没有任何东西能阻止别人使用它们。
  • single_trailing_:避免与关键字冲突。class_ 比 clss 好,不过 PEP 8 也指出,换用一个同义词可能更好。
  • __double_leading:在类的内部,这会触发名称改写(name mangling)。PEP 8 只建议在需要避免与子类中的属性冲突时使用它,并承认有些人不喜欢它。
  • __double_leading_and_trailing__:“magic”(魔法)名称,比如 __init__ 和 __file__。PEP 8 说绝不要自己发明这类名称,只使用文档中已有的。

把名称改写当作私有机制的人,常常会感到意外。它只是改了个名字,并没有上锁:

class Account:
    def __init__(self):
        self.__pin = "1234"

acct = Account()
acct.__pin            # AttributeError
acct._Account__pin    # "1234": the mangled name still works

常量只是惯例,不是规则

把名称写成大写,并不能阻止别人给它重新赋值;Python 会照常运行 MAX_RETRIES = 4。如果想让工具来强制执行,请用 typing.Final 标注这个名称:

from typing import Final

MAX_RETRIES: Final = 3
MAX_RETRIES = 4  # still runs, but mypy reports:
                 # Cannot assign to final name "MAX_RETRIES"

枚举成员是大写字母出现的另一个地方。Python 文档中的 enum HOWTO 强烈建议枚举成员名使用 UPPER_CASE,一方面因为枚举表示的是常量,另一方面也为了避免与方法名冲突:class Color(Enum) 的成员是 RED 和 GREEN。

缩写、单个字母和其他边界情况

类名中的缩写

PEP 8 规定,在 CapWords 中,缩写的每个字母都要大写,所以 HTTPServerError 优于 HttpServerError。标准库在 http.server.HTTPServer 和 json.JSONDecodeError 等地方遵循了这一点。在 snake_case 中,缩写直接小写:parse_json、http_status。

如果你用工具来改名,这一点就很重要。我们的大小写转换器会把 processOrder 变成 process_order,把 HTTPServerError 变成 http_server_error,对 snake_case 来说完全正确。但它输出的 PascalCase 会把缩写当作普通单词来写,所以 HTTP server error 会变成 HttpServerError,你需要手动把大小写改过来,才符合 PEP 8。

要避免的字母

不要使用 l(小写的 L)、O(大写的 o)或 I(大写的 i)作为单字符名称。在某些字体里,它们和 1、0 无法区分。

mixedCase

只有在 camelCase 已经是主流风格的地方,才允许使用 camelCase 的函数名,这是为了向后兼容。PEP 8 自己举的例子是 threading 模块。

为什么标准库会打破自己的规则

PEP 8 承认,Python 标准库的命名“a bit of a mess”(有点乱)。你会遇到 mixedCase 的 logging.getLogger() 和 unittest 的 assertEqual(),也会遇到小写的类,比如 datetime.datetime。list 和 dict 这样的内置类型是故意用小写的:PEP 8 为内置对象另外描述了一套约定,CapWords 只用于异常名称和内置常量。

PEP 8 里的两条规则,解释了为什么这些名称大多保持原样。第一,当已有的库采用了不同的风格时,优先保持内部一致。第二,题为“A Foolish Consistency is the Hobgoblin of Little Minds”(大意是:盲目追求一致是心胸狭隘者才有的执念)的那一节:与 PEP 8 保持一致很重要,在项目内保持一致更重要,在模块或函数内保持一致最重要。不要仅仅为了合规就破坏向后兼容。这就是 unittest 的测试类仍然要定义 setUp() 的原因:框架调用的正是这个名字。

用 Ruff 或 pep8-naming 自动检查命名

你不必靠肉眼检查命名。flake8 插件 pep8-naming 可以检查它们,Ruff 则以 N 系列规则实现了同样的检查。给定下面这个文件:

maxRetries = 3


class order_processor:
    def processOrder(self, orderId):
        totalPrice = 0
        return totalPrice


class InvalidOrder(Exception):
    pass

Ruff 会标出其中的每一个名称:

$ ruff check --select N --output-format concise orders.py
orders.py:1:1: N816 Variable `maxRetries` in global scope should not be mixedCase
orders.py:4:7: N801 Class name `order_processor` should use CapWords convention
orders.py:5:9: N802 Function name `processOrder` should be lowercase
orders.py:5:28: N803 Argument name `orderId` should be lowercase
orders.py:6:9: N806 Variable `totalPrice` in function should be lowercase
orders.py:10:7: N818 Exception name `InvalidOrder` should be named with an Error suffix
Found 6 errors.

要在整个项目中启用这些规则,请把它们加入 pyproject.toml:

[tool.ruff.lint]
extend-select = ["N"]

如果你是把文件粘贴进来,只重命名其中一个标识符,我们的查找与替换工具会显示改动了多少处匹配,在把结果粘贴回去之前,这是一个快速的合理性检查。如果要处理整个代码库,请改用编辑器的重命名重构功能,这样其他文件里的引用也会跟着改。

PEP 8 命名检查清单

  • 函数、方法、变量和参数用 snake_case。
  • 类用 CapWords,缩写的字母全部大写,异常以 Error 结尾。
  • 模块级常量用大写字母加下划线;如果希望检查工具强制执行,再加上 Final。
  • 模块名简短、全小写;包名避免使用下划线。
  • 内部名称用一个前导下划线;双下划线只用于避免子类冲突。
  • 当已有的库采用另一种风格时,与该库保持一致。

要在不同语言之间切换?我们的开发者速查表把 Python 的规则与 JavaScript、Java、Go、Rust 等语言放在一起做了对照。

© 2026 ConvertCaseHQ. 保留所有权利。