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

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

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