皇上,还记得我吗?我就是1999年那个Linux伊甸园啊-----24小时滚动更新开源资讯,全年无休!

让你的 Python 代码优雅又地道

译序

如果说优雅也有缺点的话,那就是你需要艰巨的工作才能得到它,需要良好的教育才能欣赏它。

—— Edsger Wybe Dijkstra

在 Python 社区文化的浇灌下,演化出了一种独特的代码风格,去指导如何正确地使用 Python,这就是常说的 pythonic。一般说地道 (idiomatic) 的 python 代码,就是指这份代码很 pythonic。Python 的语法和标准库设计,处处契合着 pythonic 的思想。而且 Python 社区十分注重编码风格一的一致性,他们极力推行和处处实践着 pythonic。所以经常能看到基于某份代码 P vs NP (pythonic vs non-pythonic) 的讨论。pythonic 的代码简练,明确,优雅,绝大部分时候执行效率高。阅读 pythonic 的代码能体会到“代码是写给人看的,只是顺便让机器能运行”畅快。

然而什么是 pythonic,就像什么是地道的汉语一样,切实存在但标准模糊。import this 可以看到 Tim Peters 提出的 Python 之禅,它提供了指导思想。许多初学者都看过它,深深赞同它的理念,但是实践起来又无从下手。PEP 8 给出的不过是编码规范,对于实践 pythonic 还远远不够。如果你正被如何写出 pythonic 的代码而困扰,或许这份笔记能给你帮助。

Raymond Hettinger 是 Python 核心开发者,本文提到的许多特性都是他开发的。同时他也是 Python 社区热忱的布道师,不遗余力地传授 pythonic 之道。这篇文章是网友 Jeff Paine 整理的他在 2013 年美国的 PyCon 的 演讲 的笔记。

术语澄清:本文所说的集合全都指 collection,而不是 set

以下是正文。


本文是 Raymond Hettinger 在 2013 年美国 PyCon 演讲的笔记 ( 视频 ,  幻灯片 )。

示例代码和引用的语录都来自 Raymond 的演讲。这是我按我的理解整理出来的,希望你们理解起来跟我一样顺畅!

遍历一个范围内的数字

更好的方法

xrange 会返回一个迭代器,用来一次一个值地遍历一个范围。这种方式会比 range 更省内存。xrange 在 Python 3 中已经改名为 range

遍历一个集合

更好的方法

反向遍历

更好的方法

遍历一个集合及其下标

更好的方法

这种写法效率高,优雅,而且帮你省去亲自创建和自增下标。

当你发现你在操作集合的下标时,你很有可能在做错事。

遍历两个集合

更好的方法

zip 在内存中生成一个新的列表,需要更多的内存。izipzip 效率更高。

注意:在 Python 3 中,izip 改名为 zip,并替换了原来的 zip 成为内置函数。

有序地遍历

自定义排序顺序

更好的方法

第一种方法效率低而且写起来很不爽。另外,Python 3 已经不支持比较函数了。

调用一个函数直到遇到标记值

更好的方法

iter 接受两个参数。第一个是你反复调用的函数,第二个是标记值。

译注:这个例子里不太能看出来方法二的优势,甚至觉得 partial 让代码可读性更差了。方法二的优势在于 iter 的返回值是个迭代器,迭代器能用在各种地方,setsortedminmaxheapqsum……

在循环内识别多个退出点

更好的方法

for 执行完所有的循环后就会执行 else

译注:刚了解 for-else 语法时会困惑,什么情况下会执行到 else 里。有两种方法去理解 else。传统的方法是把 for 看作 if,当 for 后面的条件为 False 时执行 else。其实条件为 False 时,就是 for 循环没被 break 出去,把所有循环都跑完的时候。所以另一种方法就是把 else 记成 nobreak,当 for 没有被 break,那么循环结束时会进入到 else

遍历字典的 key

什么时候应该使用第二种而不是第一种方法?当你需要修改字典的时候。

如果你在迭代一个东西的时候修改它,那就是在冒天下之大不韪,接下来发生什么都活该。

d.keys() 把字典里所有的 key 都复制到一个列表里。然后你就可以修改字典了。

注意:如果在 Python 3 里迭代一个字典你得显示地写: list(d.keys()),因为 d.keys() 返回的是一个“字典视图”(一个提供字典 key 的动态视图的迭代器)。详情请看 文档

遍历一个字典的 key 和 value

更好的方法

iteritems() 更好是因为它返回了一个迭代器。

注意:Python 3 已经没有 iteritems() 了,items() 的行为和 iteritems() 很接近。详情请看 文档

用 key-value 对构建字典

Python 3: d = dict(zip(names, colors))

用字典计数

更好的方法

用字典分组 — 第 I 部分和第 II 部分

更好的方法

字典的 popitem() 是原子的吗?

popitem 是原子的,所以多线程的时候没必要用锁包着它。

连接字典

更好的方法

ChainMap 在 Python 3 中加入。高效而优雅。

提高可读性

  • 位置参数和下标很漂亮
  • 但关键字和名称更好
  • 第一种方法对计算机来说很便利
  • 第二种方法和人类思考方式一致

用关键字参数提高函数调用的可读性

更好的方法

第二种方法稍微 (微秒级) 慢一点,但为了代码的可读性和开发时间,值得。

namedtuple 提高多个返回值的可读性

更好的方法

namedtupletuple 的子类,所以仍适用正常的元组操作,但它更友好。

创建一个 nametuple

unpack 序列

更好的方法

第二种方法用了 unpack 元组,更快,可读性更好。

更新多个变量的状态

更好的方法

第一种方法的问题

  • x 和 y 是状态,状态应该在一次操作中更新,分几行的话状态会互相对不上,这经常是 bug 的源头。
  • 操作有顺序要求
  • 太底层太细节

第二种方法抽象层级更高,没有操作顺序出错的风险而且更效率更高。

同时状态更新

更好的方法

效率

  • 优化的基本原则
  • 除非必要,别无故移动数据
  • 稍微注意一下用线性的操作取代 O(n**2) 的操作

总的来说,不要无故移动数据

连接字符串

更好的方法

更新序列

更好的方法

装饰器和上下文管理

  • 用于把业务和管理的逻辑分开
  • 分解代码和提高代码重用性的干净优雅的好工具
  • 起个好名字很关键
  • 记住蜘蛛侠的格言:能力越大,责任越大

使用装饰器分离出管理逻辑

更好的方法

注意:Python 3.2 开始加入了 functools.lru_cache 解决这个问题。

分离临时上下文

更好的方法

译注:示例代码在使用标准库 decimal,这个库已经实现好了 localcontext

如何打开关闭文件

更好的方法

如何使用锁

更好的方法

分离出临时的上下文

更好的方法

ignored 是 Python 3.4 加入的, 文档

注意:ignored  实际上在标准库叫 suppress(译注:contextlib.supress).

试试创建你自己的 ignored 上下文管理器。

把它放在你的工具目录,你也可以忽略异常

译注: contextmanager 在标准库 contextlib 中,通过装饰生成器函数,省去用 __enter____exit__ 写上下文管理器。详情请看 文档

分离临时上下文

更好的写法

redirect_stdout 在 Python 3.4 加入 (译注:contextlib.redirect_stdout), bug 反馈

实现你自己的 redirect_stdout 上下文管理器。

简洁的单句表达

两个冲突的原则:

  • 一行不要有太多逻辑
  • 不要把单一的想法拆分成多个部分

Raymond 的原则:

  • 一行代码的逻辑等价于一句自然语言

列表解析和生成器

更好的方法

第一种方法说的是你在做什么,第二种方法说的是你想要什么。

转自 http://python.jobbole.com/88526/

分享到:更多 ()