相比于“一门语言”,
“一门程序语言”更多时候是“一门规范”。
(当然,“语言”本身就是“规范”。)

standards

上次讲到了写代码想偷懒的话,
搞明白、想清楚再写是解决根源的一个办法。
但是凡人(没打错字)的需求始终不是重点,
写程序要偷懒,
最终还是在代码上偷懒的。

Best Practice

外国程序员很喜欢念叨一个词,
叫Best Practice。
比如说Java里面JSON要怎么处理,
就可以搜Java JSON Best Practice;
比如我对数据库一窍不通,
但我就是要学,
可以搜Database Best Practice;
比如我是甲方,
我不知道我自己想要什么,
我也可以搜Requirements Best Practice。

这种英文单词一般会有一个直译的中文,
叫最佳实践,
听起来巨蠢。
但是用来举例子就很好用。

我加入再惠的时候,
只写过一点Python,
代码习惯也是不加encoding,
不知道from __future__,
写print不写括号的,
所有要用到的第三方库都装在全局的site-packages下面。
来了以后我知道了virtualenv。
后来张总玩Lucene的时候我给他配了下Gradle,
并解释了一下:“这个东西很简单的,跟virtualenv是类似的概念。”
张总感慨道:
“其实学一门语言就是从这些工具开始啊,
这样才有一种我上手了的感觉啊!”

还有就像Go语言的gofmt命令,
这个命令会强制执行统一的代码风格调整,
不能配置、不能定制化、缩进统一使用Tab。
我十分痛苦,
不过也十分认可这里的思想:
“语言风格就是林黛玉哈姆雷特,千人千面。
相比于完美的风格,统一的风格更科学。”

Best Practice就是实际操作时的指南,
了解、掌握、实践Best Practice可以少些很多代码少走很多弯路,
用精妙的方法解决实际问题。

精妙的方法

按照套路,接下来应该讲一段精妙的方法。
不过小弟我没啥精妙的方法,
就只能举自身当反例了。

在写API的时候,
经常要处理URL,
处理URL实际上是字符串拼接。
比如Python里面把一个dict转换成query string格式,
我以前会这么写:

params = {'name': 'afu', 'action': 'take a plane'}
query_string = '&'.join(['{}={}'.format(k, v) for k, v in params.items()])  # 'name=afu&action=take a plane'

当时自我感觉良好,
觉得Python不愧是Python啊,
List Comprehension真优雅,真好看。
然而这段代码不仅有Bug,
其实Python有专门的库urllib来处理这类问题,
urllib也已经考虑了各种边界情况(比如带非法字符等):

# 举Python3为例
import urllib

params = {'name': 'afu', 'action': 'take a plane'}
query_string = urllib.parse.urlencode(params)  # 'name=afu&action=take+a+plane'

后来又学到了urlencode函数在Python2和Python3的位置都不一样,
一般是用six这个库去处理兼容性的。
下面这段代码就不需要额外说明_举Python3为例_了:

import six

params = {'name': 'afu', 'action': 'take a plane'}
query_string = six.moves.urllib.parse.urlencode(params)  # 'name=afu&action=take+a+plane'

虽然有很多种方法都能达到同样的效果,
但软件工程中,
大家往往都定下一个Best Practice然后遵守它。
这样不仅能省下写程序的功夫,
也能省下沟通争辩的功夫。
就像Zen of Python里说的一样:
There should be one-- and preferably only one --obvious way to do it.

之前我一直很好奇大部分人在用Python写datetime类型的时候,
都是用import datetime+datetime.datetime(), datetime.date()的写法,
我就一直不解,
from datetime import datetime, date+datetime(), date()这样感觉更好啊?
而且后续的代码更短。

后来读到Kenneth Reitz的Hitchhiker's Guide to Python的时候我才明白,
Explicit is better than implicit的体现就是显式指定包名,
这样代码表现力就会更强,也更易读。

## Very bad

[...]
from modu import *
[...]
x = sqrt(4)  # Is sqrt part of modu? A builtin? Defined above?


## Better

from modu import sqrt
[...]
x = sqrt(4)  # sqrt may be part of modu, if not redefined in between


## Best

import modu
[...]
x = modu.sqrt(4)  # sqrt is visibly part of modu's namespace

从学到这一点以后,
我就下定决心再也不用from datetime import datetime了。

总结

Best Practice这种东西,
看起来很美好,
用好了可以大大偷懒减少工作量。
但它有一个重要特性:
Best Practice从来不是试出来的,
而是思索、学习、择优得到的。
多加一个if,多加一个机器,多招一个人,多加一点班
可能只能解决当下的问题。
平时多学习一个,
才能到了要解决问题的时候,
面临技术、业务、上线时间的多重压力,
优雅地使用Best Practice解决问题。

总的来说,
为了偷懒 少干活提高效率,
我们又定下了这么些小目标:

  • 多学习一个Best Practice
  • 学到了就用,能用精妙的方法就不用愚蠢的方法
  • 通过思考来学习,而不是完全通过试错反馈机制来学习

毕竟编程风格可不能是散弹枪编程呀。