写出可读 Python:从 19 条"之禅"原则到日常实践

2026-05-19 30 预计阅读时间:1 分钟
来源:realpython.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:8 分钟

在 Python 解释器里敲一行 import this,屏幕会刷出 19 条英文短句——这就是著名的 Zen of Python(PEP 20)。它不是空泛的口号,而是过去二十多年里 Python 社区对"好代码"的共识提炼。理解这些原则,能帮你写出别人愿意读、愿意接手的代码。

一个 Easter Egg 的来历

1999 年,Tim Peters 在 Python 邮件列表里用半开玩笑的语气把社区长期讨论的编码哲学浓缩成了 19 条格言,发到列表后立刻被收录为 PEP 20。Python 之父 Guido van Rossum 原本也写了一条,但那条太长,最终没被采纳——于是你今天看到的第 13 条里那句"unless you're Dutch"就是 Tim 对 Guido 的调侃。

这 19 条被藏进解释器成了 Easter Egg:输入 import this 即可看到全文。运行一下:

import this

输出正是那 19 条原则。下面逐组拆解它们在工程中的含义。

可读性优先:前七条在说什么

前七条围绕一个核心——代码是给人读的:

原则 工程含义
Beautiful > ugly 代码排版、命名、结构要让人一眼觉得"舒服"
Explicit > implicit 不要靠魔法上下文推断行为,把意图写出来
Simple > complex 能用线性逻辑解决,就不要引入多余抽象
Complex > complicated 问题本身复杂时,用清晰的结构应对,而非把结构搅成一团
Flat > nested 减少嵌套层级,扁平的调用链更易追踪
Sparse > dense 一行别塞太多操作,给眼睛留呼吸空间
Readability counts 可读性不是锦上添花,是硬指标

这些原则的落地方式很具体:函数不超过 20 行、缩进不超过 3 层、一行只做一件事、用完整单词命名而非缩写。

实用主义与错误处理:第 8–12 条

  • Special cases aren't special enough to break the rules(第 8 条):别为某个"特殊场景"单独开一条逻辑分支,尽量用通用路径覆盖。
  • Although practicality beats purity(第 9 条):通用路径做不到时,务实优先。比如 str.format() 并不完美,但比纯字符串拼接实用。
  • Errors should never pass silently / Unless explicitly silenced(第 10、11 条):异常必须被看见;如果确实要吞掉,必须用显式的 except ...: pass 并写注释说明原因。
  • In the face of ambiguity, refuse the temptation to guess(第 12 条):API 设计遇到歧义时,宁可抛异常或要求显式参数,不要"聪明地"替用户猜。

唯一明显之道与时机判断:第 13–16 条

第 13 条"There should be one—and preferably only one—obvious way to do it"是 Python 与 Perl 文化分野的标志:Python 倾向提供一种标准做法,而非十种等价写法。第 14 条的 Dutch 段子提醒你——"唯一明显"对新手未必明显,文档和示例才是让之道变得明显的手段。

第 15、16 条讲时机:Now is better than never,但 never is often better than right now。翻译成工程决策——该做的事不要拖延,但仓促上线比不上暂缓打磨。发版前问自己:测试覆盖够了吗?回滚方案有吗?

设计可解释性与命名空间:第 17–19 条

  • If the implementation is hard to explain, it's a bad idea(第 17 条):如果你无法在 30 秒内向同事讲清楚某段代码的原理,这段代码就该重写。
  • If the implementation is easy to explain, it may be a good idea(第 18 条):反过来,容易解释不代表一定好,但至少通过了第一道筛选。
  • Namespaces are one honking great idea(第 19 条):模块、包、类就是命名空间。用它们隔离职责,别把所有函数扔进一个文件。

用代码对比:遵循与违背之禅

下面用一段真实场景演示"违背之禅"的代码如何被改写为"遵循之禅"的版本。场景:从 API 拉取用户列表,过滤活跃用户,返回其邮箱。

违背版本——密集、隐式、嵌套、吞异常:

import requests

def get_emails(url):
    try:
        d = requests.get(url).json()
    except:
        d = []
    return [u['e'] for u in d if u.get('a') and u.get('e')]

问题清单:变量名 d/u/e/a 隐晦;裸 except 吞掉所有异常;一行列表推导塞了过滤+取值+短路判断;嵌套隐式逻辑让人猜 aactiveeemail

遵循版本——显式、扁平、可读、错误不静默:

import requests

def fetch_active_user_emails(api_url: str) -> list[str]:
    """从 API 获取活跃用户的邮箱列表。

    Args:
        api_url: 用户列表接口地址。

    Returns:
        活跃用户的邮箱列表。API 请求失败时抛出异常。
    """
    response = requests.get(api_url)
    response.raise_for_status()  # 网络或服务端错误,绝不静默

    users = response.json()
    active_emails = []

    for user in users:
        is_active = user.get("active", False)
        email = user.get("email")
        if is_active and email:
            active_emails.append(email)

    return active_emails


# 使用示例
if __name__ == "__main__":
    emails = fetch_active_user_emails("https://api.example.com/users")
    print(emails)

改动要点:

之禅原则 改动
Explicit > implicit 变量名、参数类型、返回类型全写明
Sparse > dense 用 for 循环替代一行推导,每步清晰
Errors never pass silently raise_for_status() 让 HTTP 错误立刻可见
Readability counts 加 docstring,调用者不用猜返回格式
Flat > nested 无嵌套条件,逻辑线性展开

把之禅变成日常习惯

之禅不是背诵题,而是每次写代码时的自检清单。建议这样做:

  1. 提交前跑一次 import this——不是看输出,而是对照最近写的函数,逐条问自己是否违背。
  2. 命名审查——如果同事需要问你"这个变量什么意思",改名。
  3. 异常审查——搜索代码里所有 except:except Exception:,确认每个都有显式处理或注释说明。
  4. 嵌套审查——缩进超过 3 层的函数,拆成子函数或用 early return 扁平化。
  5. 一行审查——超过 80 字符且包含多个逻辑操作的表达式,拆开写。

最后记住第 9 条:practicality beats purity。之禅是方向,不是教条。当项目有紧迫交付压力时,先保证核心路径可读、可测、可回滚,再逐步打磨其余部分。之禅的终极目标不是完美代码,而是让团队里每个人都能高效协作的代码。


相关推荐