Pythonic 到底是什么,从 try finally 到 with 的感悟
不要费心编写 Python 已经完成的东西,因为您无法编写更好的东西。
学习 Python 的第一节课不仅仅是 “Hello World”,相信也有“Python 之禅”。当你在交互解释器中输入 import this 就会显示 Tim Peters 的 The Zen of Python:
>>>import this
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 几十年的发展过程中,禅被包裹了一层又一层,混杂了更多东西,最后出现了 Pythonic 这个新创的单词,是 Python 之禅的解释和体现。
就像 ComputerHope 网站解释的那样:
Pythonic 是一个形容词,它描述了一种与 Python 编程语言的创立哲学一致的计算机编程方法。在 Python 中有很多方法可以完成相同的任务,但是通常有一种首选的方法可以完成此任务。这种首选方式称为“Pythonic”。
但是这对于大家来说依然是一个笼统的概念,就像迷雾之中的灯塔,让人看不清楚,就算有一天偶尔触摸到了,你可能也意识不到。
只有直接通过 Python 的方法解决一个问题的时候,或许才会明悟。
try finally
下面就说说我从try finally 到 with 中学到的 Pythonic:
2019年的时候,工作需要我接触了 xlwings 这个 Python 库,当然我是使用的 Python2.7 版本。
简单介绍一下该库,不然下面讲不下去,xlwings 库是一个可以直接调用电脑上 Excel 的接口的库,用户可以通过 xlwings 新建 Excel 表格、修改、计算其中的数据等等。
不同于其他处理 Excel 表格的库,由于 xlwings 是直接在后台调用 Excel 程序来打开需要处理的表格,所以处理过程中,我在任务管理器中是可以看到一个 Excel 程序被启动了。
当然这没有什么问题,xlwings 很强大,Excel 有的功能基本上都能调用,也没有其它库关于 xls、xlsx 这种弱智的文件版本问题,但是:在代码运行过程中报错的话,后台的 Excel 程序不会自动销毁,而会变成僵尸程序,永远留在系统中除非人为干预删除。
这就很搞笑了,留了一个 Excel 程序在后台,打开任务管理器可以看到:
然后我试着重新打开那个 Excel 文件,会出错,错误提示:在另一个应用程序打开。请将其关闭,然后重试。
所以这个 Excel 文件直接就不能正常打开了,xlwings 当然也不能打开这个表格了,因为这个表格已经被后台中的僵尸程序占用了,所以我只能在任务管理器中将 Excel 进程关闭,然后才能继续操作。
非常麻烦,代码在编写过程中运行出错是很常见的,难道每出错一次就手动终止进程一次?
所以使用 try - finally 语句块,在 finally 语句下写退出代码,这样就算是报错还是怎么样都是没有问题的,程序都会退出(除非你连退出程序的代码都写错了)。
这里还是放上示例代码吧:
# -*- coding:utf-8 -*-
import xlwings as xw
import os
try:
path = "test.xlsx"
app1 = xw.App(visible=False,
add_book=False)
app1.display_alerts = False
wb = app1.books.open(path)
wb_sheet1 = wb.sheets[0]
cell = wb_sheet1.range("A1")
cell.nonono()
cell.value = 2
wb.save()
finally:
app1.quit()
那么这就是 Pythonic 的写法嘛?当然不是,我从来不觉得加了 try- finally 就很优雅了,因为像 try-else-except-finally 这些是程序语言的错误处理机制,在很多语言中都存在,并且个人不是很喜欢设置过多的缩进级别。
我只是为了能让程序跑起来,先整上再说。
with
而真正将其写的比较 Pythonic 是几个月后吧,应该:
后来频繁的使用 xlwings 来处理 Excel 表格,我想把涉及到的代码模块化,方便复用,可以就是由于这个烦人的 try-finally 语句块导致读取模块化工作做不出来,因为 try 和 finally 是相连的,且finally 中就是程序退出代码(可能我技术有限吧,先把这句话说出来,免得被喷)。
这怎么能行呢?思来想去,就像前面说的:
“Pythonic 是一个形容词,它描述了一种与 Python 编程语言的创立哲学一致的计算机编程方法”
我直接好家伙,使用上下文管理器不就行了嘛,一个 with 解决一切烦恼,这不就是保持与 Python 编程语言的创立哲学一致嘛!
使用上下文管理器就可以自动在程序结束后、或者出错后,自动将后台程序退出。并且只用 with 即可,上下文使用非常广泛,比如 with open(file) as p:
,见过吧。
创建上下文管理器有两种方式,一种是使用内置方法 __enter__
和 __exit__
实现,我没用过;
我使用的是第二种较为简单的方法,名称比较长:上下文管理器装饰器;就像名字一样,添加一个装饰器再添加一个 yield 关键字就行了。
看看下面这个例子:
# -*- coding:utf-8 -*-
from contextlib import contextmanager
import xlwings as xw
@contextmanager
def open_xlwings(input_file, output=None):
app = xw.App(visible=False, add_book=False)
app.display_alerts = False
app.screen_updating = False
print "input_file:", input_file
wb = app.books.open(input_file)
print "File Name: {}".format(app.books)
print "Pid: {}".format(app.pid)
sheet = wb.sheets[0]
yield sheet # 返回 sheet
print "Quit Xlwings"
app.quit()
被装饰器装饰的函数分为三部分:
with语句中的代码块执行前执行函数中yield之前代码;
yield返回的内容复制给as之后的变量;
with代码块执行完毕后执行函数中yield之后的代码。
看看下面这个简单例子及其输出结果就明白了:
import xlwings as xw
import os
from glwings import open_xlwings
path = "test.xlsx"
with open_xlwings(path) as p:
# code
print "ok"
输出结果
input_file: test.xlsx
File Name: Books([<Book [test.xlsx]>])
Pid: 11788
ok
Quit Xlwings
但是这样还是不行哦,如果 with 语句块的代码出错的话,后续的也会直接停止的,所以在上下文管理器装饰器所修饰的函数中需要加入错误处理模块:
# -*- coding:utf-8 -*-
from contextlib import contextmanager
import xlwings as xw
@contextmanager
def open_xlwings(input_file, output=None):
app = xw.App(visible=False, add_book=False)
app.display_alerts = False
app.screen_updating = False
print "input_file:", input_file
wb = app.books.open(input_file)
print "File Name: {}".format(app.books)
print "Pid: {}".format(app.pid)
sheet = wb.sheets[0]
try:
yield sheet # 返回 sheet
except Exception as e:
info = "with an error {}".format(e)
raise
else:
if output:
wb.save(output)
else:
wb.save()
finally:
print "Quit Xlwings"
app.quit()
还是非常简单的,现在就可以非常安心的使用 with 语句来操控 xlwings 打开 Excel 表格了。
其实像这种功能 xlwings 库应该自己就需要实现的,不知道为什么没有支持 with 的文件对象,当然咯我说的是对应 Python2.7 版本的 xlwings,像现在最新的 Python3 版本对应 xlwings 怎么样了我就不清楚了,不过多半没什么变化,如果能支持早就支持了,不会等到现在。
如果以后有人问你,什么是 Pythonic,你叫他多写 with 就可以了,这就是一种 Pythonic 的写法,非常的纯正,非常的 Pythonic;而编写5、6层嵌套的简直是在犯罪。
优美(美丽)是Python 之禅的第一个词,这就是我理解的 Pythonic。
这是一篇半心得体会半技术的文章,心得体会部分充斥了本人的主观臆断,不可全取;技术部分可以小心参考。