文章目录
引言
有一个代码可读性陷阱无处不在,一旦你意识到它,就很容易避免:他就是 参数单位的省略
。
观察下面三个程序:
Python
time.sleep(300)
Java
Thread.sleep(300)
Haskell
threadDelay 300
它们分别延时了多长时间?Python程序延时了5分钟,Java程序延时了0.3秒,Haskell程序延时了0.3毫秒。
如何从上面的代码看出来呢?根本做不到。你必须把这些函数的用法记下来:time.sleep
的单位是秒,threadDelay
的单位是微秒。如果你经常查阅文档,就能记住这些知识,但是我们怎样才能让那些没有遇到过 time.sleep
的人也知道它的用法呢?
方式1:把单位放在参数名中
不要这样写代码:
def frobnicate(timeout: int) -> None:
...
frobnicate(300)
这样做吧:
def frobnicate(*, timeout_seconds: int) -> None:
# * 强制后面的参数在调用时使用关键字参数
...
frobnicate(timeout_seconds=300)
在第一种情况下,我们甚无法通过函数调用看出 300 是超时时间,即使我们知道 300 是超时时间,但它的单位呢?毫秒?秒?天?相比之下,第二种情况的函数调用不需要额外说明也能看懂。
对于支持命名参数的语言来说,使用命名参数是一个好办法,但这并不总是可行的。在 Python 中, time.sleep
的延时时间取决于 secs
参数,但由于函数实现的原因,我们不能像 sleep(secs=300)
这样调用 sleep
函数,在这种情况下,我们可以给该值一个名称。
不要这样写代码:
time.sleep(300)
应该这么做:
sleep_seconds = 300
time.sleep(sleep_seconds)
现在,代码的含义是十分清楚的,不需要查阅文档就能读懂。
方式2:使用强类型
将单元放入参数名中的另一种方法是使用比int
或float
更强的类型。例如,我们可以使用timedelta
类型。(译者注:如果某编程语言没有内置这种类型,我们也可以自定义该类型)
不要这样写代码:
def frobnicate(timeout: int) -> None:
...
frobnicate(300)
应该这么做:
def frobnicate(timeout: timedelta) -> None:
...
timeout = timedelta(seconds=300)
frobnicate(timeout)
对于一个给定的浮点数,你需要在被告知单位的情况下才能理解它的意义。如果幸运的话,它的单位在变量名或参数名中,但如果不幸的话,这只会在文档中说明或者根本没有说明。对于像timedelta
这样的参数类型,我们都知道它的意义,这样做也消除了代码的模糊性。
应用范围
上面的建议并不局限于变量和函数参数,它还适用于API、metric names、序列化格式、配置文件、命令行参数等。除了最常见的timeout
单位,它还适用于货币金额、长度、数据大小等。
例如,不要像这样编写返回结果:
{
"error_code": "E429",
"error_message": "Rate limit exceeded",
"retry_after": 100,
}
用下面的方式替代:
{
"error_code": "E429",
"error_message": "Rate limit exceeded",
"retry_after_seconds": 100,
}
不要这样设计你的配置文件:
request_timeout = 10
建议像下面这样:
request_timeout = 10s
request_timeout_seconds = 10
还有,不要这样设计你的命令行程序:
show-transactions --minimum-amount 32
建议这样设计:
show-transactions --minimum-amount-eur 32
或
show-transactions --minimum-amount "32 EUR"
后记
第一次尝试翻译技术类文章,可能会有一些纰漏,翻译的不好还请各位大佬多多见谅。
原文:Please put units in names or use strong types | written by Ruud van Asseldonk