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 |
| 类 | CapWords | InvoiceSender |
| 异常 | CapWords;如果是错误,以 Error 结尾 | PaymentDeclinedError |
| 函数、方法 | snake_case | send_invoice() |
| 变量、实例变量 | snake_case | total_price |
| 方法的第一个参数 | self;类方法用 cls | def from_settings(cls, settings) |
| 常量 | 大写字母加下划线,通常放在模块级别 | MAX_RETRIES |
| 类型变量 | 简短的 CapWords | T、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):
passRuff 会标出其中的每一个名称:
$ 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 等语言放在一起做了对照。


