如何写出符合python之禅的代码

1. python之禅

在交互式解释器里输入指令

import this

便可得到python之禅

The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

网络上有很多翻译的版本,我比较喜欢下面这个版本

Python之禅 by Tim Peters
 
优美胜于丑陋(Python 以编写优美的代码为目标)
明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
可读性很重要(优美的代码是可读的)
即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)
 
不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写 except:pass 风格的代码)
 
当存在多种可能,不要尝试去猜测
而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
虽然这并不容易,因为你不是 Python 之父(这里的 Dutch 是指 Guido )
 
做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)
 
如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
 
命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)

2. 如何理解和应用

网上能够找到的文章,千篇一律的翻译了原文而已,最多是加上一些自己的解读,却让人看的云里雾里,并没有实际的例子可以供读者学习理解。

在收集了查阅了一些技术文章后,我决定综合各路大神的见解加上我个人的一些看法,结合着具体例子来阐释python之禅的含义,个人能力有限,如文章观点有纰漏之处,欢迎你讨论指正。

2.1 明了胜于晦涩

下面用两种方式向百度发送一个get请求
方法1, 使用urllib库

import urllib.request
res = urllib.request.urlopen('http://www.baidu.com/')
print(res.read())

方法2,使用requests库

import requests
res = requests.get("http://www.baidu.com")
print(res.text)

对比两段代码不难发现,第二段代码的代码意图更加明显,明确指明了要发送get请求,而第一段代码呢,你无法从代码里直观的观察出是在发送get请求

2.2 简洁胜于复杂

仍然使用urllib库和requests库进行对比,下面两段代码都是发送post请求

1、使用urllib

import urllib.parse
import urllib.request
import json

url = 'http://www.httpbin.org/post'
values = {'name' : 'Michael Foord'}

data = urllib.parse.urlencode(values).encode()
response = urllib.request.urlopen(url, data)
body = response.read().decode()
print(json.loads(body))

2、 使用requests

import requests

url = 'http://www.httpbin.org/post'
data = {'name' : 'Michael Foord'}

response = requests.post(url, data=data)
response.json()

对比两段代码,简洁程度高下立判,任何一个技术人员都倾向于选择后者作为自己的努力方向,没有人愿意阅读并维护一大段啰里啰嗦的代码。

2.3 可读性很重要

2.3.1 简单就好

如果你的代码是简明的,简洁的,那么代码就已经具备了可读性了,但这还不够,还有许多可以改进的地方,比如下面的这段代码

for i in range(len(lst)):
    print(lst[i])

通过索引访问列表元素在语法上没有问题,但阅读时不免觉得代码有些繁琐,下面的代码的可读性就强一些

for i in lst:
    print(i)

2.3.2 变量命名

python在声明变量时,不需要指明变量的类型,在接触python之前,我的主力语言是c++, 刚接触python时,对这种变量创建方式很不习惯,其他语言,很容易顺藤摸瓜找到变量声明的地方确认变量的类型,而python则不行,所以,我习惯性的在变量名称中加上类型的标识,比如下面这样

name_lst = ['小明', '小红']

stu_dict = {'name': '小明', 'age': 20}

除了像age,count这样明显表示int类型数据的变量,我喜欢用 "i_" 开头, 表示它是int类型的数据,我认为这样做可以增强代码的可阅读性。

2.3.3 代码注释

说道可阅读性,就不得不说代码注释,我个人非常喜欢给代码添加注释,我希望代码的维护者能知道这段代码的意图,而不是在维护代码时不敢修改,生怕产生连锁bug。好的注释,要解释代码做了什么,如果有必要,还要解释为什么要这样做,这对于维护者修改代码是非常有帮助的。

2.4 扁平胜于嵌套

for循环,while训话,if语句,这些都会产生嵌套,每产生一层嵌套,就会产生一次缩进,最终,就会产生类似下图的代码

据说这是Spring的一段源码,之前还有新闻说,一个程序员在工作群里贴出这段代码被开除。

嵌套最好控制在3层以内,太多了,你自己看着都会烦。

2.5 间隔胜于紧凑

一行代码里,不要做太多的事情,这样做,带给人一种错觉,好像这样可以加快代码的执行速度,实际上是不可能的。

下面是一段紧凑的例子

def func_1(a, b):
    return a*b


def func_2(a, b):
    return a+b


def func(a, b):
    return func_1(a, b) if a < 10 else func_2(a, b)

函数func 看起来很简洁,但实际上并不利于阅读,这一行代码里有一次if条件判断,决定执行两个函数中的哪一个,我自己很不喜欢这种方式,我会这样来写

def func(a, b):
    if a < 10:
        res = func_1(a, b)
    else:
        res = func_2(a, b)

    return res

前面是从一行代码的逻辑复杂程度来阐述间隔胜于紧凑的,从代码排版上来说,我喜欢在for循环,while循环,if条件语句前面加上一个空行,因为在这些语句之前,通常来说都是准备数据的代码,这些语句块结束后,也放一个空行,这样代码看起来就会显得错落有致,不至于乱糟糟的黏糊在一起。

优美胜于丑陋,啥是优美呢,这是个哲学问题,每个人对于美的理解是不一样的,但总的来说,工整总归是比混乱美一些,这种代码排版上的用心会产生工整美。

2.6 不要包容所有错误

类似下面的代码,你肯定见过很多

try:
    do something
except:
    处理异常

这种写法的一个好处就是,写代码的人,根本不需要关心代码里面发生了什么异常,来一个终极捕获,万事大吉了,剩下的事情就是记录异常信息。

这中写法有什么问题么,至少在实践中,我还没有遇到什么严重的问题,但我自己也很清楚,这样做是极不合理的,原因在于,自己写的代码,自己都不清楚可能会有什么异常产生,这不就等于像世人宣告,自己不了解自己写的代码么?

如果你了解自己的代码,怎么可能不知道可能发引发哪些异常呢?你辩解说,那么多异常,谁知道会发生什么呢,可是你是技术人员,经验的积累难道还不足以让你评估代码潜在的异常么?胡子眉毛一把抓式的捕获异常,并不能够让我们更加了解自己的程序,也不能让我们提高对代码的掌控能力,除了在时间紧任务重时帮我们快速完成工作外,这种做法不能带来任何其他收益。

3. 写在最后

本文尝试结合实际例子阐述python之禅,但能力有限,总感觉有些力不从心,写在最后,是想感慨一下,国内技术人员的心态真的不如国外,你能搜索到的文章,都是在翻译python之禅,却鲜有人举例说明,认真研究一下,就这么难么?

当然,你可以说这都是房价太高导致的,人人都在想着怎么赚钱,哪有心思踏踏实实做学问啊,哎,我没有买房子,我只是想写一些有价值的东西,写别人懒得较真,懒得打破砂锅问到底的东西,希望我可以坚持下去。

2.1,2.1 小节的内容,参考了http://codingpy.com/article/designing-pythonic-apis/ 的文章内容,文章原文是以色列的一名python开发者所写,标注出处,是因为不想窃取别人的翻译成果。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案