命名 — 代码唯一的说明书¶
这一章讲一件事: 怎么起名字。你可能会奇怪一本编程书为什么第一章(真正的第 2 章)先讲这个—— 作者的回答是:命名是软件开发里最重要、也最难的事之一,而几乎所有编程书都把它跳过了。 读完你会知道:好名字的标准、Python 的命名规矩,以及注释和 docstring 各自该写什么。
1. 这一章讲什么¶
作者在章首把理由说得很直白:你可能会惊讶这本书以命名开篇——「你大概期待的是代码,但无可否认, 命名是软件开发中最重要、也最困难的事情之一。」1
每个变量、函数、类都有一个名字。这个名字一般就是你读代码时唯一的描述—— 你读到名字,就该知道那个对象是干什么的。就像听到有人喊你的名字,你知道是在找你; 如果旁边还有个同名的人,就会一片混乱。2
由此推出本章的全部内容:
名字是唯一的说明
└─ 所以坏名字 = 不可读代码(def a(b, c))
└─ 所以好名字要「按场景选长度」(price_of_apple / price / p 谁对?看上下文)
└─ 所以 Python 立下拼写规矩(snake_case、CamelCase、下划线),让名字的形状也有信息
└─ 所以注释和 docstring 只能补名字的不足,不能替名字干活
图说:这一章是一条单向论证链,每一步都是「名字是唯一的说明」的推论。
2. 顶层全景:一个名字的三次选择¶
起一个名字,实际要做三次选择。本章按这个顺序讲:
| 选择 | 问题 | 本章哪节 |
|---|---|---|
| 选含义 | 这个名字让人想到的东西,和它实际干的事,一致吗? | 第 3 节的规则 |
| 选长度 | 多长的名字才够?什么时候 p 反而比 price_of_apple 好? |
第 3 节的规则 |
| 选形状 | 大小写和下划线怎么摆(规矩是社区约定,不是语言强制)? | 第 4 节的约定 |
主线例子贯穿全章:同样一个「苹果的价格」,三个名字 price_of_apple、price、p,
作者的判断是——都对,取决于上下文。3
3. 核心原理:好名字的规则¶
3.1 坏名字与好名字:同一段代码的两个版本¶
解决什么问题: 让读者不看实现就知道代码在干什么。
怎么做: 书里给了对照。坏版本:
读到这行,你对它在干什么一无所知,只能去读实现——「极其耗时且容易出错」。4 好版本:
make_appointment 这个名字让你大概率不用读实现就知道它干什么——「这就是好名字的力量」。5
3.2 长度按场景选:同一个对象的三个合法名字¶
解决什么问题: 名字不是越长越好;机械地追求「描述性」会得到 price_of_apple_including_tax_for_the_year_2026。
怎么做: 走一遍主线例子。一个变量存「苹果的价格」,三个候选:3
price_of_apple # 通用场景:描述性够、又不算太长,默认选它
price # 在水果摊的上下文里:你已经在谈苹果,够了
p # 在遍历价格列表时:for p in prices: —— 只用一两行,上下文说清了它是谁
场景 需要的名字长度 例子
通用/跨函数 长 price_of_apple
局部上下文清晰 中 price
只用一两行的循环变量 短 p
图说:名字长度的本质是「把上下文没说的部分补上说」;上下文说得越多,名字可以越短。
为什么有效、边界在哪: 这条规则保护的是读者的工作记忆——名字太长,读者要解析的东西就多; 名字太短而上下文又没兜住,读者就要停下来猜。猜,正是第 01 章禅句里明令拒绝的事。 边界:判断「上下文够不够清晰」没有公式,只能靠经验——这也是作者说命名难的原因。
3.3 最小惊讶原则与「现实里叫什么,代码里就叫什么」¶
作者给的两条通用规则:
- 现实里存在的东西,代码里用同一个名字。 业务里说「预约」,代码里就
make_appointment,不要发明暗号。 - 最小惊讶原则(rule of the least astonishment): 如果一辆车有个
fly方法,读者会很惊讶—— 「惊讶在软件开发里从来不是好事,惊讶只会带来 bug。」6
顺带一条自检:如果你忍不住要给变量或函数起一整行的名字,说明这段代码装的功能太多,该拆了。 函数只有一个任务时,好名字最好找(这个判断在第 10 章展开)。
3.4 书里的约定应用示例,逐行看¶
原书在第 4 节给出了一段「约定示范代码」,值得逐行看——每一行都是本章规则的一次应用:8
# 函数和变量:全小写,单词间下划线
def add_two_numbers(a, b):
pass
my_useful_variable = 3.14
# 类:每个单词首字母大写,连写
class MyCar:
pass
三行各自示范一件事:add_two_numbers 是 snake_case 的函数名;my_useful_variable 是 snake_case 的变量名
(数值 3.14 是值,名字里一个数字都没有——还记得「想给名字加数字是坏信号」吗);MyCar 是 CamelCase 的类名。
看完这三行,第 4 节的约定表就不用背了——约定本来就是「看到形状就知道种类」的肌肉记忆,不是考试知识点。
3.5 不要用缩写¶
「不要用在场的读者可能不理解的任何缩写……省下的那几个字符,不值得它们造成的困惑。」
只有行业内广泛通行的缩写(比如物理公式里的 v 表示速度,书里 speed_limit(v) 就用了)才允许。7
4. Python 的命名约定:形状即信息¶
解决什么问题: 约定让名字的形状本身携带信息——看到 MyCar 你知道是类,看到 MY_CONSTANT 你知道是常量。
怎么做: Python 的约定记录在 PEP 8 里,本章挑了承重的那部分:8
| 对象 | 约定 | 例子 | 强制吗 |
|---|---|---|---|
| 变量、函数 | 全小写,单词间下划线,叫 snake_case | my_useful_variable、add_two_numbers |
只是约定 |
| 类 | 每个单词首字母大写、连写,叫 CamelCase(骆驼式) | MyCar |
只是约定,但强烈推荐遵守 |
| 常量 | 全大写 | MY_CONSTANT |
约定;Python 根本无法把变量变成常量,全大写只是给人看的信号 |
| 私有成员 | 单个前导下划线 | _my_private_method |
约定;解释器不拦外人调用,但「高度不建议」无视它 |
三条约定各配一句书里的原话式提醒:「只是约定,不是解释器强制」——作者在常量那行连说了两遍, 因为新人最常见的误会就是以为全大写或下划线有什么魔法:它们只对人起作用,机器一视同仁。8 真要把常量「锁死」,Python 做不到;能做的只有(如第 01 章所说)让名字替你喊话,再靠 code review 和 lint 工具盯着。
作者强调了三遍「这只是约定,解释器不强制」——这正是 Python 的一贯作风(回想第 01 章的类型提示)。 另有两条禁令:名字不要用数字开头、不要双下划线开头(双下划线保留给特殊方法),也不要用 Unicode 字符(给全世界文字统一编号的字符标准)甚至 emoji 起名—— 技术上可行,实际是自找兼容性和可读性的麻烦。9
还有一个隐蔽的坑:不要覆盖内建函数。比如自己定义一个名叫 print 的函数,之后 print 就再也打印不了东西了——
对自己和对读者都是混乱之源。10
5. 注释与 docstring:补名字的不足¶
解决什么问题: 总有些信息名字装不下。但注释泛滥是病——「好代码自己能说清楚,不需要注释。」
怎么做: 作者的分工划得很清:
| 东西 | 给谁看 | 该写什么 | 本章作者的立场 |
|---|---|---|---|
注释(# 之后) |
读代码的开发者 | 只写「为什么」,不写「是什么」——是什么该由名字讲 | 「学着不看注释读代码」;书里示例的注释他「自己也不引以为傲」 |
| docstring(三引号字符串) | 代码的外部使用者 | 写「是什么」:函数/类/模块的对外说明 | 当作外部文档写;内部沟通读代码本身 |
docstring 是个特殊存在:解释器不忽略它(普通注释会被忽略),但也不执行它;
它挂在对象的 __doc__ 属性上供人查阅。11
为什么有效、边界在哪: 这个分工的底层逻辑是:注释和名字会过期(改了代码忘了改注释,注释就变成谎言), 而「为什么」变化得慢。边界:作者的态度偏严苛——「几乎不写注释」是理想态,很多团队会保留更多注释; 这里的分寸是「每多一条注释,就多一处会过期的信息」,自己权衡。
docstring 的访问方式也顺便给出——它挂在一个叫 __doc__ 的属性上:
def make_appointment(date, duration):
"""预约一个时段。date 是日期,duration 是分钟数。""" # ← 这就是 docstring
...
print(make_appointment.__doc__) # 预约一个时段。date 是日期,duration 是分钟数。
普通注释解释「为什么」、docstring 解释「是什么」、名字解释「这是谁」——三者分工完毕, 剩下的就是第 10 章的问题了:函数本身要短到「读名字加 docstring 就够」。
6. 编程到底在干什么:本章的落点¶
本章中途插了一节「What Programming is Really About」,配了一张工程师时间分配的图: 你绝大部分时间在读代码、理解代码,写代码只占一小部分。12
这就是命名被放在全书最前面的终极理由:既然读是主要活动,那么决定读速的第一变量就是名字。 顺带的推论还有个词——重构(refactoring):在不改变功能的前提下改进代码结构; 作者说,如果测试足够(第 15 章),「重构可以非常快乐」;不做的话,代码会变成「Big Ball of Mud(大泥球)」—— 一个专门形容「毫无结构、改哪里都怕」的系统的行话。13
6.1 书里那个「引以为耻」的注释示例¶
原书在注释一节给了个难得一见的自我示范:他给一段代码加了注释,然后明说—— 「这些注释我加得并不自豪,只是为了让你看懂这段代码在干什么;但这通常是坏实践,应该避免。 你应该学会不靠注释读代码。」11
这段自白比任何规则都更能说明他的立场:注释不是禁止品,而是拐杖——新手期拄着走可以, 但目标是扔掉。判断一根注释该不该留的终极问题:「换个更好的名字、抽个小函数,能不能替代它?」 能,就动手改代码;不能(通常是「为什么选这个奇怪写法」的历史原因),才轮到注释。
6.2 重构为什么「快乐」¶
这个词值得多停一秒,因为它在本章和后面章节反复出现。重构能快乐,前提是有测试兜底: 改结构不改行为,怎么证明「行为没变」?唯一的方式就是跑一遍测试,全绿才敢继续。 所以「重构快乐」和「测试充足」是绑在一起的——这也是作者把测试放在语言课之后的真正用意: 不是流程完整性的要求,是让修改变得安全的许可证。 没有这个许可证,重构就是徒手拆承重墙;有了它,重构只是搬家具。
7. 作者的判断与证据¶
书里给了证据的: 坏/好名字的对照代码可直接读;三个长度梯度的例子可直接跑; PEP 8 的链接与约定条目是可查证的外部标准。8
作者的判断(无理由化、靠经验断言):
- 「命名是软件开发中最难的事情之一」——无法证明,但行业经验普遍支持;
- 「命名约定之间的优劣差别很小,别试图说服同事换约定」——这是个务实的停战宣言;14
- 「不要写注释」的强度——明显是他个人口味的较强版,一般团队不会走到这么绝。
判断(我们的,不是书里的): 这章最值得带走的是「名字长度 = 上下文补全量」这个公式, 它比「见名知意」四个字可操作得多。而「几乎不写注释」我们建议打折执行: 在团队代码和跨团队接口处,解释「为什么」的注释值得写,只是在写之前先问自己「换个名字能不能解决」。 如果错,会错在: 如果项目的新人流动率极高、读代码的人完全无法接近作者, 那么多注释的维护成本确实会压倒收益——那时作者的铁律才对。
8. 边界与局限¶
- 本章没讲怎么改坏名字(rename 的具体操作、IDE 工具),只给了原则。
- 「私有成员靠约定」在 Python 里是真的,但团队大、约束要强时,需要 lint 规则或代码评审兜底,本章没提。
- 命名与领域建模(第 07 章「和市场部说同一套词」)的关系在这里只点了一句。
8.1 「好名字」的完整判据清单¶
把本章分散的标准收拢成一张可执行的检查表,起名卡住时逐条过:
| 判据 | 问自己的问题 | 出处 |
|---|---|---|
| 真实性 | 现实里存在的东西,代码里用同名吗? | §3.3 规则一 |
| 最小惊讶 | 读者看到这个名字,第一反应会猜错吗? | §3.3 规则二 |
| 长度 | 上下文替它说了多少?补够了吗? | §3.2 |
| 无缩写 | 这个词行业外的人认识吗? | §3.5 |
| 形状对 | snake_case / CamelCase / 大写常量用对了吗? | §4 |
| 无冲突 | 覆盖内建函数了吗?撞关键字了吗? | §10 |
| 可替代性 | 需要注释解释「是什么」吗?需要就说明名字不够好 | §6.1 |
七条全过,名字就是好名字;卡住任何一条,先修名字再往下写。这张表是我们对全章规则的归拢。
9. 可带走的¶
本章主线例子,一行写完: 同样存「苹果的价格」——通用代码写 price_of_apple,
水果摊上下文写 price,循环里写 p;三个名字都对,判据是「上下文替它说了多少」。3
- 名字是读者唯一的说明;读代码的时间远多于写代码,所以命名优先于技巧。
- 长度公式: 名字要长到「任何读者都明白对象是什么」;长到一整行,说明代码该拆了。
- 最小惊讶原则: 现实叫什么代码叫什么;
car.fly()这种惊喜只会带来 bug。 - 不用缩写(行业内通行的除外),省字符不值得换困惑。
- 形状即信息: 变量函数用 snake_case,类用 CamelCase,常量全大写,私有成员单下划线——全是约定,不强制。
- 别覆盖内建函数(比如自己定义
print);别用 emoji 起名。 - 注释写「为什么」,docstring 写「是什么」;理想态是两者都很少——先换名字,再写注释;
docstring 用三引号,挂
__doc__上,给外部使用者读。 - 别为命名约定打仗——项目里已有的约定,跟着用。
- 好名字的七条判据:真实、不惊讶、长度合适、无缩写、形状对、无冲突、不需要注释解释——卡住任何一条,先修名字。
- 遇到读不懂的坏名字,别猜——回到本章 §3 的判据重构它,重构时测试兜底(第 15 章)。
10. 命名规矩的边角,一次说清¶
原书把几条零碎规矩散在各处,收在一起省得回头找:
关于数字。 名字可以含数字,但数字不能做第一个字符(value2 行,2value 不行);
而且作者「一般建议不要在变量名里用数字」——想给名字加数字,通常是坏信号:该是数字的是变量的值,不是名字。
(x1、x2 这类临时循环记号除外,那是行业习惯。)
关于关键字和内建。 if、else、while、for、def、class 这些关键字(语言自己占用的词)
不能用作名字,解释器会直接报错;而内建函数(print、list、sum……)可以被覆盖——语言不拦,
但后果立竿见影:
「不仅让你困惑,也让读你代码的每一个人困惑。」10
关于项目的约定压倒个人口味。 这一章最后,作者专门花了一段说:如果你加入的项目用的是另一套命名约定, 别试图说服同事换成你学的那套。 理由很朴素:全部改一遍的成本太高,而不同约定之间的差别「很小」。 所以「入乡随俗」是命名约定的最高原则——本章讲的 snake_case 只是「乡俗之一」。14
一个容易被忽略的细节:下划线开头的「私有」是单边约定。 Python 不拦外人访问 _my_private_method,
纯粹靠「大家都会自觉」维持。这意味着两件事:读别人的代码,看到单下划线就该警觉「这是内部实现,可能随时改」;
写自己的代码,把真正想藏的东西藏进方法后面(第 11 章的封装),而不是指望下划线替你挡人。15
11. 原文地图¶
这张表按全章出现顺序排列主题,方便读完正文后回头抽查任何一条论断。
它也是跳回原书的传送门:主题、原书章名、精确到段的地址,三列齐全,逐条可核、可跳转、可复验;每条地址都可用 node scripts/book-verify.mjs python-made-easy 回核。
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 为什么开篇讲命名 | 2 Naming | text/06-ch02-2-naming.txt:3(搜「start this book with the chapter on naming」) |
| 名字是唯一的说明 | 2 Naming | text/06-ch02-2-naming.txt:7(搜「description of an object」) |
def a(b, c) 坏名字 |
2 Naming | text/06-ch02-2-naming.txt:17(搜「def a(b, c)」) |
make_appointment 好名字 |
2 Naming | text/06-ch02-2-naming.txt:34(搜「make_appointment」) |
| 三长度梯度(price_of_apple/price/p) | 2 Naming | text/06-ch02-2-naming.txt:44(搜「price_of_apple」) |
| 读多写少的时间分配 | 2 Naming | text/06-ch02-2-naming.txt:62(搜「most of your time reading」) |
| 重构与 Big Ball of Mud | 2 Naming | text/06-ch02-2-naming.txt:71(搜「Big Ball of」) |
| 命名规则(数字、双下划线、Unicode、关键字、覆盖内建) | 2 Naming | text/06-ch02-2-naming.txt:80(搜「double underscore」) · text/06-ch02-2-naming.txt:92(搜「overriding built-in」) |
| snake_case / CamelCase / 常量 / 私有下划线 | 2 Naming | text/06-ch02-2-naming.txt:102(搜「snake_case」) · text/06-ch02-2-naming.txt:107(搜「CamelCase」) · text/06-ch02-2-naming.txt:114(搜「single underscore」) |
| 注释:只写为什么 | 2 Naming | text/06-ch02-2-naming.txt:131(搜「code after a # is ignored」) · text/06-ch02-2-naming.txt:155(搜「why the code does something」) |
docstring 与 __doc__ |
2 Naming | text/06-ch02-2-naming.txt:148(搜「triple quotes」) |
| 最小惊讶原则、别用缩写 | 2 Naming | text/06-ch02-2-naming.txt:166(搜「least astonishment」) · text/06-ch02-2-naming.txt:178(搜「abbreviations」) |
| 别劝同事换约定 | 2 Naming | text/06-ch02-2-naming.txt:141(搜「persuade your coworkers」) |
-
出处:「2 Naming」第 3 段(
text/06-ch02-2-naming.txt:3,搜「start this book with the chapter on naming」)。 ↩ -
出处:「2 Naming」第 6–10 段(
text/06-ch02-2-naming.txt:7,搜「description of an object」;第 9 段,搜「same name」)。 ↩ -
出处:「2 Naming」第 42–57 段(
text/06-ch02-2-naming.txt:44,搜「price_of_apple」;第 55 段,搜「prices =」)。 ↩↩↩ -
出处:「2 Naming」第 17–23 段(
text/06-ch02-2-naming.txt:22,搜「extremely time-consuming」)。 ↩ -
出处:「2 Naming」第 34–38 段(
text/06-ch02-2-naming.txt:36,搜「power of a good name」)。 ↩ -
出处:「2 Naming」第 164–168 段(
text/06-ch02-2-naming.txt:166,搜「least astonishment」)。 ↩ -
出处:「2 Naming」第 176–181 段(
text/06-ch02-2-naming.txt:178,搜「abbreviations」)。 ↩ -
出处:「2 Naming」第 96–118 段(
text/06-ch02-2-naming.txt:100,搜「recorded in」;PEP 8 链接见脚注text/06-ch02-2-naming.txt:111,搜「pep-0008」)。 ↩↩↩↩ -
出处:「2 Naming」第 79–95 段(
text/06-ch02-2-naming.txt:80,搜「double underscore」;第 85 段,搜「emojis」)。 ↩ -
出处:「2 Naming」第 92–94 段(
text/06-ch02-2-naming.txt:92,搜「overriding built-in」)。 ↩↩ -
出处:「2 Naming」第 146–156 段(
text/06-ch02-2-naming.txt:148,搜「doc」)。 ↩↩ -
出处:「2 Naming」第 59–66 段(
text/06-ch02-2-naming.txt:62,搜「most of your time reading」)。第 66 行注明此节配图是「Coding activities of a software engineer」。 ↩ -
出处:「2 Naming」第 69–75 段(
text/06-ch02-2-naming.txt:70,搜「improving the structure」;「Big Ball of Mud」的链接脚注见第 75 段)。 ↩ -
出处:「2 Naming」第 140–144 段(
text/06-ch02-2-naming.txt:142,搜「way too much effort」)。 ↩↩ -
出处:「2 Naming」第 114–118 段(
text/06-ch02-2-naming.txt:114,搜「single underscore」;「约定、不强制」见第 116–118 段)。 ↩