Names should be as short as possible while still being clear

Ben Hoyt

命名应在清晰的前提下尽可能简短

原文由 Ben Hoyt 发布,订阅该博客

这篇小短文基于我之前关于 Python API 设计的一场演讲中的一部分,不过我认为其中的核心观点适用于各种编程语言。

曾在网景工作的程序员 Phil Karlton 有句名言:“计算机科学中只有两件难事:缓存失效和命名。”

给事物命名其实很容易,难的是把名字起。也许正因为命名太容易,我们才不愿花更多时间去挑选好名字。

有些开发者确实会把名字起得太短。不过我认为,更常见的错误是把名字起得过长。

名字太长会让人难以抓住重点。举个例子,假设刚毕业的 Bob 来设计 Requests 库,而他的某位老师曾说过“给函数起个能说明它做什么的名字”。

这建议本身不算差,但他却听成了“给函数起个能说明它所做一切的名字”,于是写出了这样的代码:

# requests.py
def send_get_request_and_receive_response(url):
    ...

调用起来是这样的:

import requests

response = requests.send_get_request_and_receive_response(url)

这有什么问题呢?多余的词并没有增加任何信息,反而削弱了清晰度:requests.get() 一目了然、易于阅读,而用那个长名字时,关键信息——也就是它是“get”方法这一点——却被藏在了名字的中间。

“request”这个词出现了两次,一次在模块名里,一次在函数名里。还有“send”和“receive response”。但这个库本身就是用来发送请求和接收响应的,所以这些词完全是多余的。

最后剩下简洁的 requests.get()——它清晰而精炼地表达了“发起 GET 请求”的意思。已经不能再短了。当然,你也可以把它写成 requests.g()——但那就太离谱了。

一直主张把模块设计成通过“模块名加点号前缀”的方式来使用。Requests 正是如此:单看 get() 可能不知所云,但 requests.get() 就清晰明了。也不会出现命名冲突,因为模块名就在那里起到了区分作用。

换句话说,不要为了把一件事的方方面面都解释清楚而命名;要让名字在上下文中意思清晰。

使用简洁的名字不仅在设计库时重要,在编写日常代码时同样重要。局部变量名通常可以很短,因为上下文已经由周围的代码——也就是它所在的函数或脚本——提供了。

来看一个例子。最近我在 repository_statistics.py 这个脚本里看到了这样一段代码:

repository_index_retriever = RepositoryIndexRetriever(args.architecture)
repository_statistics_processor = RepositoryStatisticsProcessor()
for repository_object in repository_index_retriever.retrieve_repositories():
    for repository_file in repository_object.repository_files:
        repository_statistics_processor.increment_repository_file_count(repository_file.name)

for repository_file_name, count in repository_statistics_processor.get_top_n_repositories(10):
    print(repository_file_name, count)

全是大词!简单的逻辑淹没在了冗长的措辞里。

下面是我会改写的方式(顺便说一下,RepositoryStatisticsProcessor 不过是 collections.Counter 的一个花哨包装,我们就直接用它吧):

fetcher = repolib.IndexFetcher(args.arch)
counts = collections.Counter()
for repo in fetcher.get_repos():
    for file in repo.files:
        counts[file.name] += 1

for repo, count in counts.most_common(10):
    print(repo, count)

结构完全一样,但逻辑现在要清晰易懂得多。

而在 Python 中,你经常会写单行的列表推导或生成器表达式。写这些时,你甚至可以更进一步,使用单字母的变量名——当定义和使用都在同一行时,f 或许和 filefile_object 一样清晰。

例如,我们可以用 Counter.update 配合生成器表达式和单字母变量名来改写内层循环:

counts.update(f.name for f in repo.files)

最后,我想用 Go 项目的技术负责人 Russ Cox 的一句话来收尾,他这样阐述自己的变量命名理念:

名字的长度不应超过它所承载的信息量。……全局名称需要传达相对更多的信息,因为它们会出现在更多不同的上下文中。即便如此,简短而精准的名字也往往比冗长的名字更能说明问题:对比一下 acquiretake_ownership。让每个名字都有意义。

最后一句堪称诗意。不过,我还是要用自己更平实的一句话来总结我的“变量命名理念”:

命名应在清晰的前提下尽可能简短。

本文章由 muse-spark-1.2-contributor 进行翻译

评论