Python 之禅 Zen of Python
Python 之禅(Zen of Python)是 Python 社区最广为人知的一份设计哲学,共 19 条格言,出自 Tim Peters 之手,收录于 PEP 20。它并非语法规则,却深刻影响了 Python 语言本身的演进方向与整个生态的编码风格。只要在交互式解释器里敲下 import this,这份格言就会以彩蛋的形式出现在屏幕上。
前置知识
阅读本节前,建议先了解:启动文件与配置
基础概念
什么是 Zen of Python
Zen of Python 是一组以「A is better than B」句式为骨架的短句,用来表达 Python 设计者认为更重要的价值取向。它回答的不是「怎么写才不报错」,而是「同样能实现功能的两段代码,哪种更符合 Python 的精神」。
| 项目 | 内容 |
|---|---|
| 出处 | PEP 20 – The Zen of Python |
| 作者 | Tim Peters |
| 收录版本 | CPython 2.2.1 起内置(this 模块) |
| 定位 | 信息型提案(Informational),非强制规范 |
| 查看方式 | 交互式解释器执行 import this |
| 格言数量 | 19 条 |
它与 PEP 8 的区别
初学者容易把这两份文档混为一谈,其实分工清晰:
| 维度 | Zen of Python(PEP 20) | PEP 8(Style Guide) |
|---|---|---|
| 性质 | 哲学与价值取向 | 可执行的风格约定 |
| 颗粒度 | 抽象、需要理解 | 具体、可逐条对照 |
| 强制力 | 无 | 社区约定 + 工具检查 |
| 举例 | 「可读性很重要」 | 「缩进用 4 个空格」 |
| 能否自动化 | 不能 | 能(ruff、black、flake8) |
一句话:Zen 告诉你为什么,PEP 8 告诉你怎么做。
一个容易被忽略的事实
import this 的实现并不「简单直白」——它把 19 条格言用 ROT13 编码后写进源码,运行时才解码打印。这本身就是一句自嘲:连「Python 之禅」自己都没有完全遵守「简单胜于复杂」。
# 查看 this 模块的实现源码
import inspect
import this
print(inspect.getsourcefile(this)) # 定位 this.py 路径语法与用法
查看格言的几种方式
# 方式一:交互式解释器或脚本中
python -c "import this"
# 方式二:直接运行标准库模块
python -m this
# 方式三:查看模块源码
python -c "import this, inspect; print(inspect.getsource(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!用程序读取格言
this 模块内部把文本存在 this.s 变量中(已解码),可直接读取:
"""以编程方式获取并统计 Python 之禅的格言。"""
import this
def load_aphorisms() -> list[str]:
"""从 this 模块中提取 19 条格言。"""
# this.s 是已经解码的完整文本,按行切分后过滤空行与标题
lines = [line.strip() for line in this.s.splitlines()]
return [line for line in lines if line and not line.startswith("The Zen")]
def summarize() -> None:
"""打印格言数量与最长的一条。"""
aphorisms = load_aphorisms()
print(f"格言总数:{len(aphorisms)}")
longest = max(aphorisms, key=len)
print(f"最长的一条:{longest}")
if __name__ == "__main__":
summarize()详细说明
前 7 条:关于表达与结构
前 7 条集中回答「代码应该长什么样」。
可读性很重要(Readability counts) 是整份文档的枢纽,其余几条大多服务于它。
# 不推荐:紧凑但难读,一行塞进多个操作
data = [x ** 2 for x in range(10) if x % 2 == 0 if x > 2]
# 推荐:条件拆开,意图清晰
data = [
x ** 2
for x in range(10)
if x % 2 == 0 and x > 2
]显式优于隐式(Explicit is better than implicit) 体现在 Python 的诸多设计里:import 必须写明模块名、异常必须显式抛出、没有 PHP 那种自动类型转换带来的隐藏行为。
# 错误!依赖隐式行为,读者无法一眼看出返回类型
def fetch_count(raw):
return raw and len(raw)
# 推荐:显式声明输入输出类型与空值处理
def fetch_count(raw: list[str] | None) -> int:
"""返回记录条数,输入为 None 时返回 0。"""
if raw is None:
return 0
return len(raw)扁平优于嵌套(Flat is better than nested) 与 稀疏优于密集(Sparse is better than dense) 共同反对「金字塔式缩进」和「一行写完所有逻辑」。
第 8~9 条:规则与实用的张力
特例不足以特殊到打破规则(Special cases aren't special enough to break the rules) 与 实用性胜过纯粹性(Although practicality beats purity) 是一对张力。前者要求一致性,后者承认现实妥协。
典型的现实妥协是 float 与 int 的运算:纯数学上 1 + 1.0 应当类型不匹配,但 Python 选择隐式提升为 float,因为它更实用。
# 语言层面的实用主义:int 与 float 混算自动提升
print(1 + 1.0) # 1.0
print(type(1 + 1.0)) # <class 'float'>第 10~12 条:错误与歧义
错误不应被静默(Errors should never pass silently) 要求失败必须可见。除非被显式静默(Unless explicitly silenced) 则允许你主动、有意识地吞掉异常。
# 错误!裸 except 会把所有错误吞掉,包括 KeyboardInterrupt
try:
value = int("abc")
except:
pass
# 推荐:只捕获预期异常,并给出处理或明确注释
try:
value = int(user_input)
except ValueError:
value = 0 # 输入非法时回退到默认值,属于显式静默面对歧义,拒绝猜测的诱惑(Refuse the temptation to guess) 是 Python 不支持函数重载、不做隐式类型转换的理由之一。
第 13~14 条:唯一明显的方式
应该有一种——最好只有一种——明显的方式去做(There should be one obvious way to do it) 是 Python 与 Perl 哲学(TIMTOWTDI,条条大路通罗马)最鲜明的分野。第 14 条的「除非你是荷兰人」是玩笑——Python 之父 Guido van Rossum 是荷兰人。
现实情况是这条格言常被批评「理想化」,因为字符串格式化就有 %、str.format、f-string 三代写法并存。但这恰恰说明了格言的用意:新增写法时应当淘汰旧写法,而不是无限堆积。
name = "World"
age = 18
# 旧:% 格式化(Python 2 风格)
print("%s is %d" % (name, age))
# 中:str.format(Python 2.6+)
print("{} is {}".format(name, age))
# 新:f-string(Python 3.6+,推荐)
print(f"{name} is {age}")第 15~16 条:时机
现在总比永远不做(Now is better than never) 鼓励尽早动手。然而永远不做常常又好过现在就做(Although never is often better than right now) 则提醒:急于求成可能做出错误承诺。这两条同样是一对张力,常用于 API 设计「要不要现在就加这个参数」的权衡。
第 17~18 条:可解释性
如果实现难以解释,那它就是个坏主意(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)。
注意第 18 条用的是「可能(may be)」,比第 17 条的「是(is)」弱。好解释只是必要不充分条件。
# 不推荐:用自研缓存装饰器绕来绕去,难以向同事解释
_CACHE: dict = {}
def memoize(fn):
def wrapper(*args):
key = (fn.__name__, args)
if key not in _CACHE:
_CACHE[key] = fn(*args)
return _CACHE[key]
return wrapper
# 推荐:使用标准库,语义清晰、附带线程安全等考量
from functools import lru_cache
@lru_cache(maxsize=None)
def expensive(n: int) -> int:
return n * n第 19 条:命名空间
命名空间是个绝妙的主意,让我们多用一些(Namespaces are one honking great idea) 是 Python 模块系统的设计基石。import 带来的隔离、__ 双下划线名称改写、包与子包的分层,都是这条格言的落地。
import json
import os.path # 子模块作为独立命名空间
# 不推荐:把整个模块的内容粗暴导入当前命名空间
# from json import *
# 推荐:保留命名空间前缀,来源一目了然
payload = json.dumps({"path": os.path.join("a", "b")})
print(payload)这 19 条不是法律条文
需要强调的是:Zen of Python 内部就存在矛盾(第 8 条 vs 第 9 条、第 15 条 vs 第 16 条)。它的价值在于提供讨论代码质量的共同语言,而不是当作可以机械套用的判分标准。
"""演示:同一条格言在不同场景下结论可能相反。"""
def join_csv_fast(rows: list[list[str]]) -> str:
"""性能优先:用生成器表达式 + join,紧凑但可读。"""
return "\n".join(",".join(row) for row in rows)
def join_csv_readable(rows: list[list[str]]) -> str:
"""可读优先:显式循环,便于加日志与调试。"""
lines: list[str] = []
for row in rows:
lines.append(",".join(row))
return "\n".join(lines)
if __name__ == "__main__":
sample = [["a", "b"], ["c", "d"]]
# 两者结果完全一致,选择取决于团队与场景
print(join_csv_fast(sample) == join_csv_readable(sample)) # True实战示例
示例一:用格言审视一段真实代码
下面这段函数同时违反了多条格言,我们逐条对照改进。
"""反例与正例对照:基于 Zen of Python 重构。"""
# 错误!违反「显式优于隐式」「可读性很重要」「扁平优于嵌套」
def g(d):
r = []
for k in d:
if d[k]:
if isinstance(d[k], list):
for i in d[k]:
r.append(k + ':' + str(i))
return r改进后:
"""根据格言重构:显式类型、扁平结构、清晰命名。"""
def flatten_active_fields(mapping: dict[str, list[str]]) -> list[str]:
"""把 {字段: [值, ...]} 展开为 ['字段:值', ...] 列表。
只处理非空的列表值。
"""
result: list[str] = []
for field, values in mapping.items():
if not values:
continue # 显式跳过空值,而不是嵌套 if
result.extend(f"{field}:{value}" for value in values)
return result
if __name__ == "__main__":
data = {"tags": ["py", "zen"], "empty": []}
print(flatten_active_fields(data)) # ['tags:py', 'tags:zen']示例二:写一个「格言自检」小工具
把格言变成可勾选的清单,用于代码评审前自查。
"""代码自查清单:把 Zen of Python 转成可交互的评审项。"""
from dataclasses import dataclass
@dataclass(frozen=True)
class CheckItem:
"""一条自查项。"""
key: str
question: str
CHECKLIST: tuple[CheckItem, ...] = (
CheckItem("explicit", "所有函数参数与返回值是否显式标注类型?"),
CheckItem("flat", "嵌套层级是否控制在 3 层以内?"),
CheckItem("readable", "变量名是否无需注释就能看懂?"),
CheckItem("errors", "捕获异常时是否只捕获预期类型?"),
CheckItem("one_way", "同一功能是否只保留一种实现方式?"),
)
def render_checklist(answers: dict[str, bool]) -> str:
"""根据作答渲染 Markdown 清单。"""
lines: list[str] = ["| 项目 | 结论 |", "|------|------|"]
for item in CHECKLIST:
mark = "通过" if answers.get(item.key) else "待改进"
lines.append(f"| {item.question} | {mark} |")
return "\n".join(lines)
if __name__ == "__main__":
result = render_checklist({"explicit": True, "flat": False, "readable": True})
print(result)示例三:用 python -m this 做命令行彩蛋
在团队工具里,可以为不小心敲错命令的同事留一个提示。
"""CLI 兜底提示:找不到子命令时提示查看 Python 之禅。"""
import argparse
import this # noqa: F401 —— 导入即触发打印,属于模块副作用
def build_parser() -> argparse.ArgumentParser:
"""构建仅用于演示的参数解析器。"""
parser = argparse.ArgumentParser(description="演示命令行兜底")
parser.add_argument("command", nargs="?", help="子命令名称")
return parser
def main() -> int:
parser = build_parser()
args = parser.parse_args()
if args.command is None:
print("未指定子命令。以下是 Python 之禅,供你稍作沉思:")
# this 在导入时已经打印过,这里仅作流程说明
return 0
print(f"执行子命令:{args.command}")
return 0
if __name__ == "__main__":
raise SystemExit(main())注意事项
- Zen of Python 是设计哲学而非语法约束,违反它不会报错,但会降低代码在社区中的可维护性。
- 19 条格言之间存在真实矛盾,遇到冲突时应结合项目上下文判断,不要机械套用。
- 第 13 条「唯一明显的方式」常被误读为「不允许有第二实现」。它的本意是新增替代方案时应同步淘汰旧方案。
- 不要把「简单胜于复杂」当作少写类型标注、少写错误处理的借口——那是「简单」被滥用为「简陋」。
import this会产生副作用(打印内容),在库代码里导入它会导致意外的标准输出,不要这么做。- 第 14 条的「除非你是荷兰人」是社区玩笑,不必当作技术论据引用。
最佳实践
- 把 Zen of Python 当作代码评审的共同词汇,讨论时用「这里偏隐式了」代替「我不喜欢这样写」。
- 与 PEP 8 配合使用:Zen 定方向,PEP 8 定细节,工具(ruff、black)负责执行细节。
- 在团队新人入职材料中收录这 19 条,并用本项目的真实代码片段举例说明。
- 遇到第 8 条与第 9 条冲突时,优先看改动成本:一致性收益大就守规则,实用性收益大就例外,并记录原因。
- 用第 17、18 条作为技术选型的快速筛子:一个自研方案如果需要长篇文档才能解释清楚,先去找标准库或成熟库。
下一节
下一节:PEP 流程与索引