0%

Python 编码规范

PEP,全称 Python Enhancement Proposals,译为 Python 增强提案。PEP 已经成为 Python 发布新特性的主要机制,它会收集社区对 Python 的改进意见,经过核心开发者的审查和认可最终形成提案向公众公示。PEP 的官网首页 也是 PEP 0 的地址,在这里官方列举了所有的 PEP 的索引,你可以按序号、标题和类型进行检索。

Python 之禅

Python 开发者喜欢用 “Pythonic” 这个单词来形容符合 Python 编码风格的代码。这种风格既不是严格的规范也不是编译器强加给开发者的规则,而是大家在使用 Python 语言协同工作的过程中逐渐形成的习惯。要记住:Python 开发者不喜欢复杂的事物,他们崇尚直观、简洁而又易读的代码。为此,Python 语言的早期贡献者 Tim Peters 提出了 PEP 20 – The Zen of Python,译为 Python 之禅,提出了共计 19 条 Python 编码的指导性原则。这已经作为一个彩蛋加入到 Python 标准库中,你可以在 Python 交互式命令行中敲入 import this 查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

这 19 条指导思想强调了代码简约可读的重要性,其中的大多数条目不仅仅适用于 Python,也适用于任何一门其他语言。

Python 风格指导

除此之外,PEP 8 – Style Guide for Python Code 也是每个 Python 程序员应当阅读的,相较于 Python 之禅它提出了更为细致的建议,目的是让 Python 程序员遵循一致的编码风格。PEP 8 中的大部分都能在 Pycharm IDE 中找到智能提示,缩进、空格与空行也可以通过代码格式化快捷键(Reformat Code)来一键规范化,在 Mac OS 中默认快捷键为 Cmd + Alt + L,Windows 中为 Ctrl + Alt + L。如果你不使用 PyCharm,也可以安装 Pylint,这是一款 Python 源码静态分析工具,可以自动检测代码是否符合 PEP 8 风格指南。

命名规范

这里,我想强调一下 Python 中的命名规范。PEP 8 提倡采用不用的命名风格来区分 Python 语言中的不同角色:

  • 文件名(模块名)使用小写字母,单词间以下划线连接,如 base_futures.py;私有模块使用单个下划线开头,如 _collections_abc.py
  • 函数、变量及属性名,使用小写字母,单词间以下划线连接,如 dict_keys
  • 受保护的属性和函数(子类可以访问),使用单个下划线开头,如 _protected_method
  • 私有的属性和函数(子类也不能访问),使用两个下划线开头,如 __private_method
  • 类与异常,以每个单词首字母大写来命名,如 BaseHandlerTypeError
  • 模块级别的常量,全部用大写字母,单词间以下划线连接,如 STDIN_FILENO
  • 类中的实例方法(instance method),首个参数命名为 self 表示对象自身;类方法(class method),首个参数命名为 cls 表示类自身。

有几点需要说明的是,Python 中下划线前缀仅仅是个约定,由于 Python 没有 public、protected、private 等访问权限控制关键字,只能以有没有下划线开头这种约定俗成的规范告诉程序员这个变量或函数的范围,注意这并不是强制约束。即使函数以下划线开头,在导入模块后仍能够通过 dot 运算符直接访问。

1
2
3
4
5
>>> import another
>>> another.external_func()
This is a external_func.
>>> another._internal_func()
This is a _internal_func.

但需要注意的是,如果通过 * 通配符导入的模块,单下划线以及双下划线开头的函数和属性并不会被导入到当前模块中,除非导入模块显式定义了包含这些函数和属性的 __all__ 列表(但通常不会这么做)。此外,也不建议通过通配符导入模块,应当按照最小导入原则,显式的导入需要用到的函数和属性。

1
2
3
4
5
>>> from another import *
>>> external_func()
This is a external_func.
>>> _internal_func()
NameError: name '_internal_func' is not defined

在命名时,尤其要避免以双下划线开头且结尾的命名格式,如 __foo__,这是 Python 内置的魔法方法(magic methods,或称特殊方法,如 __init__),以及内置属性(如 __code__)的命名方式。因为你不能保证在后续版本中 Python 不会将 __foo__ 作为内置方法或属性。

如果你阅读 Python 标准库源码,会发现基本上私有命名都是以单下划线开头,不论是私有函数还是私有类或是私有变量和常量,很少会看到以双下划线开头的。PEP 8 也提倡对于非公有方法和属性使用单个下划线开头,只有在避免子类命名冲突时才采用双下划线开头(且不以双下划线结尾),这是由于双下划线前缀会导致 Python 解释器改写属性名称(name mangling)。比如下面代码中的 __v3 就被改写为 _Foo__v3 类名 + 变量名的格式:

1
2
3
4
5
6
7
8
9
>>> class Foo:
... v1 = 1
... _v2 = 2
... __v3 = 3
... __v4_ = 4
... __v5__ = 5
...
>>> [_ for _ in dir(Foo) if 'v' in _]
['_Foo__v3', '_Foo__v4_', '__v5__', '_v2', 'v1']

上面代码使用一个单独的下划线 _ 作为循环中的变量名称,代表这个变量是临时的,名称无关紧要,你可以将其理解为占位符

PEP 8 还提到,对于与 Python 保留关键字命名冲突的公有属性,可以采用单个下划线结尾的命名格式,这要优于使用缩略格式。比如下面的 class_ 变量:

1
tkinter.Toplevel(master, class_='ClassName')

另外,Python 在维持语义清晰的原则上为了保证简洁性,一些简短的介词和连词间会省略下划线,并没有严格的按照单词间下划线连接,而是直接拼接,比如 isinstance__setattr__getstate