什么是pythonic的思考方式

最近在学习大神 slatkin 的高效编程指南,发现有很多细节以往自己都不曾注意过但却是非常值得了解的。在这里总结并分享给大家。


1.遵循PEP8的编程风格


 PEP8全称《Python Enhancement Proposal #8》,又叫做8号python增强提案,通过规范编程风格,使得自己的代码更加易懂,不同的开发人员之间可以更高效地沟通。完整指南见:https://www.python.org/dev/peps/pep-0008/。比较常用的一点建议是:添加缩进时尽量使用空格,tab键在不同的编程环境下,可能代表不同的空格数,不一定是四个空格。另外,Python的宗旨是:每件事都应该有直白的做法,而且最好只有一种。


2.注意bytes、str、unicode的区别


首先需要了解的一点是:计算机在硬盘中存储的数据格式以及网络中传输的数据均为bytes. 比如,你的电脑D盘中有个名为test.txt的文本文件,那么这个文件中的数据是以bytes的类型存储在硬盘中的。以Python3为例,当你将text.txt中的数据(文本或者数字),读入内存,进行一些特定的处理或计算时,你需要将这些bytes类型的数据转换为Unicode类型,然后在你的Python代码中尽情地执行各种计算和处理。同理,处理好之后的内容,你可能想写出到硬盘中去保存起来,那么你应该再将Unicode类型的数据转为bytes类型。

目前unicode和bytes之间相互转换的编码方式主要采用UTF-8,Python3中提供了encode()和decode()方法来实现编码转换。

上图代码的意思是:‘中文’ 这两个汉字在硬盘中是以 b'\xe4\xb8\xad\xe6\x96\x87' 存储的。当它被解码(decode)之后,就以字符串(str) ‘中文’ 的形式展示。了解这些可以帮助我们在操作文件时,避免因为编码混乱的问题,使得程序出现一些奇怪的报错信息。

在工程实践中,我们在一段代码中因需要的不同可能想操作不同的数据类型,比如有些情况下我们想操作二进制的bytes(如图像像素),有些情况下我们想操作字符串str (如处理一些文本),这个时候,利用辅助函数在整个核心代码块的外围将数据统一为我们需要的格式,会使得程序更灵活,例如,假设当前的核心代码块有可能接受str为其输入数据,也有可能接受bytes为其输入,为了保证核心代码块内部操作的数据是一致的(不妨假设为str),可以通过如下辅助函数实现:


3.要善于使用辅助函数


 Python的语法特别精炼,因此我们很容易使用一行代码来实现一些很精巧的操作,但是不建议过度使用这种特性,除非你编写代码不是为了某个项目需要,而是为了炫技。辅助函数也使得多次利用重复的逻辑功能变得十分便捷。

比如上图中这个字符串解析的例子,method1和method2都实现了从解析结果中查询某个参数的功能,但method1通过辅助函数使得代码更加易懂,并且可以重复调用,查询不同的参数时,只需要更改参数‘red’一次即可,而method2每更换一次查询参数,至少需要改动三处才能实现。


 4.熟练掌握序列切割的技巧


 Python的切片操作相信很多人一开始接触Python就已了解了一些,这里只标注三点作为示例:

(1)采用-1做步进值,可以很容易将一个序列颠倒

(2)切割列表时,范围可以越界,Python会智能识别,但是用下表访问元素时,不能越界

 

(3)Python支持用不同长度的列表去覆盖原列表中的某些片段


5.用列表推倒取代map和filter函数


在Python中,列表,字典,集合均支持推倒操作,并且比使用map和filter函数更加清晰,原因是map和filter函数需要编写lambda函数,下面是一例子。(当然,很复杂的一些推倒操作使用列表推倒可能会比较吃力)

上图中列表解析法显然清晰易懂,而采用map函数的话则要编写两次lambda函数,并且采用filter函数进行一次过滤操作。另外,当列表解析式变得比较复杂时,最好将推倒过程拆成两个及以上表达式实现,总之,宗旨是使得代码简单易懂。


6.善于利用生成器改写数据量较大的列表推导


在Python中,列表推导的一个毛病是:在推导的过程中,所有被处理过的数据都会临时存储在内存中,举个例子,如果你的硬盘中有一个文件,现在你要读取这份文件并返回每一行的字符个数,那么Python会把文件中的每一行的长度数据保存在内存中。当这个文件很大时,就会特别耗费内存。这个时候采用生成器表达式来代替列表推导,是一种很明智的选择。建议读者去本人另一篇博文 【Python的迭代器和生成器】处回忆一下生成器的概念。


7.合理使用try/except/else/finnaly结构


先看一个例子:

 

无论try模块是否发生异常,最后的finally模块都会进行清理工作,是一种保障;

如果try块发生了异常,则except模块会进行异常报告;

如果try块没有异常,(当然except块就不执行),else块会接着完成程序剩下的功能;else块缩减了try块的代码量,起到了分担任务,是代码结构清晰的作用;

各司其职。

 


 

参考:

《effective Python》,Brett Slatkin

廖雪峰的官方网站 https://www.liaoxuefeng.com

posted @ 2017-08-24 20:26  Ping的博客  阅读(367)  评论(0编辑  收藏  举报