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

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函数在Python2Python3的位置都不一样,
一般是用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.

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

后来读到Kenneth ReitzHitchhiker'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
  • 学到了就用,能用精妙的方法就不用愚蠢的方法
  • 通过思考来学习,而不是完全通过试错反馈机制来学习

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