Pulpcode

捕获,搅碎,拼接,吞咽

0%

如何写出有质量的python代码

Introduction

本来标题是“如何写出一个高质量的python代码”,后面想想,还是别夸自己了,没有那本事,标题就改为 “如何写出有质量的python代码”,我觉得更应该叫,写有一定质量的python代码。

实际上有质量的python代码是一个很大的话题,一本书估计都写不完,我仅仅自我总结而已。

多考虑前期需求分析和设计

之前的我花1天的时间设计,花一个多星期去实现,现在我花一个星期去做设计(uml和文档),花一两天去实现。这件事,编码的年龄越大,领悟越透彻。

写好异常

异常本身不算是python特有的,这本身算是编程的基本功了,那我就从异常本身和python特性两方面,来谈谈异常。

不要乱打

不要所有错误都打异常,如果你现在连,if else,异常,和断言的区别都不清楚,那就要好好恶补了。实际上异常是用来抓住那些未知错误的,也就是那些不确定的错误,你的代码里如果有逻辑错误,那是不能用异常去抓的。类似:“文件读写,网络操作,数据库操作等”,这些需要跟硬件打交道的,才需要异常处理。这也就是说,你的代码中如果有上述的处理,那么一定要写好每一个try catch, 但是实际情况是大多人的代码都是“裸操作”。

常见的使用异常错误是:

  1. 把异常写到最外面,然后catch一个异常基类。如果这样,那你就把所有异常都抓了!我建议每个异常处理,只抓一个操作。
  2. 抓住一个A异常,要么不做任何处理,要么抛出一个B异常,这些都是思路不清晰的做法。既没有处理好异常,也没有暴露问题,既没有在运行时发现问题,还增加了调试的难度。
  3. 把异常直接返回给客户端,这一来不友好,二来,让人知道你的系统内部怎样设计的。实际上在我的server里,请求我,如果出现异常,我都是:打异常日志,报警,然后返回给客户端的结果为:“服务器处理中”或者“服务器内部错误”。

在实践中,你就会发现,如果你想做好这几点非常难,而且做好这几点,又能把代码写漂亮的,就更难了。当然要优先考虑业务实现,再考虑代码封装。

几个例子

1
2
3
4
5
6
7
8
9
10
# 使用Python的requests库做http请求
try:
r = requests.post(url, headers=headers, data=data, timeout=timeout)
r.raise_for_status()
except requests.RequestException:
logging.error('网络访问异常', exc_info=True)
......
else:
logger.info("签名请求返回成功:{}".format(r.content))
......

还有需要注意的是,我很鄙视那些写print e的人,知不知道什么叫异常栈啊,你这样打出来的东西,能分析个卵啊?如果是我,我会这样处理。
如果是日志:logging.error('xxxxxxx', exc_info=True)
如果是直接打印到屏幕,那就:traceback.print_exc()

明白异常不算主流程

实际上异常是要考虑,但是你要明白真正的异常状况所发成的频率到底是多少,你自己想想,一年自己去过几次医院?

数据库操作

数据库的异常部分,我上面已经描述了,这里提一下:update

对于一个update操作,除了之前提到的try和catch,一定要在update之后,检查一下row change的个数,很多bug,最后才发现数据库状态并没有改变,就是因为没有检查row change,还有一个在业务级别需要考虑的是,修改类似,状态status这样的字段时,一定要先判断当前状态再去改变,以符合状态机流程。

还有些细节问题,就是不要拼sql。保证自己代码安全。

挡住输入

如果你得到的输入,不是你程序内部产生的,而是从文件,从客户端的请求中获取到的,那么你一定要对输入进行筛选和校验。类似字段校验,类型校验,长度范围校验。动态语言,更要考虑这些。

不要把QA当单测

很多人根本没想过自己的代码为什么写的这么烂,原因就是把QA当单测,实际上你的代码质量,只能靠你自己来保证。

动态语言

“不是语言的问题,是人的问题”,这是很多高估自己的软件工程师,爱说的一句话。实际上,java和c#不适合互联网行业,但是传统软件行业,是离不开这种静态语言的。天生的静态类型检查和诸多限制,是维持行业稳定的标志。而python缺这个,所以它开发快,但是代码要差很多。实际上会python的人,编码差距也大很多(牛人很贵的好么?),比如某个牛人走了之后,后来的那个人,根本不知道如何维护,大多数情况就是重写。如果项目上了一个几十万行的数量级,那个时候只能通过工具分析的错误时,python就傻了。

你的类属性和方法不用像java那样通过继承得来,也就说没有编译期的类型检查。你那些乱扔进去的属性,IDE都智能补全不了。你的接口也只能通过扔NotImplement的方式让其强制继承。很多java需要实现的设计模式,在python中实现,也变得随意很多。

写业务快,带来的后果就是项目做大,这个“空中楼阁”就难以维护了。当然,你可以继续说:“是人的问题”,那么你为啥不用lisp去写呢?

我觉得先要明白python是什么,适合做什么。

多做业务级别的封装,而非代码级别的封装

这个问题也是我最近在编写代码是思考和改进的地方,我们在封装代码的时候,大部分人的封装都是代码级别的,而非业务级别的,你从你函数,或者类的命名就可以发现,如果总是写parsedata,gendata,dataformat的函数。说明你的框架在做的也是代码级别的重用和封装,那么什么才是业务级别的封装呢?我觉得大概是这样:

1
2
3
4
5
6
# 通知上层系统
notify_upper_system

# 获取一条待处理收款记录
fetch_collect_record_processing

符合python的特性

举个栗子吧,对于一个这样的操作:

1
2
3
4
if c not in d:
return n
else:
return d[c]

这样写就可以了

d.get(c, n)

如果你不知道python的这些特性,自然“用python写c代码”。

写好python注释

不要玩代码

我们大多数的代码是业务代码,不是github上的开源项目,或者教学代码,或者博客上的实例代码。所以,不要有递归,不要有装饰器,不要有猴子补丁,不要有元类,不要用这些证明自己很屌,除非你会用那就直接用,如果你仅仅知道这个东西,仅仅想用在你的项目中时,你还是不要用了。

其它的如何打好日志,如何设计好项目结构,如何用好第三方库的,我的博客都有写,不在叙述。