相信用 C 语言写过档案读取的人都知道,经常开档后就忘记关档,或者程式中间跳出例外,因此没有关档。这些问题常常让人非常头痛?。
所幸 Python 中有 with
这个语法糖,可以自动帮你开关档,跳出例外也难不倒他,真的非常好用。
但你真的了解 with
背后的运行原理吗? ?
不知道没关係,因为你现在就会知道了!
context manager
在讲 with
之前,必须先介绍一下 context manager,中文可以翻成 情境管理器。
为什么叫做 情境管理器 呢? 我们试想一个情境...
当你进入房间时,就要开启房间的灯:然后当你离开房间时,就要关闭房间的灯。
像这样理所当然一定要做的事情,我们就称为一个情境。而 Python 就是透过情境管理器处理这些情境。
要自己实作 context manager 其实很简单,只要在 Class 中实作 __enter__()
和 __exit__()
即可。
就以进入房间当作例子。
class Room(): def turn_on_light(self): print("开灯") def turn_off_light(self): print("关灯") def __enter__(self): print("进入房间") self.turn_on_light() return "在房间里" def __exit__(self, exc_type, exc_value, traceback): self.turn_off_light() print("离开房间")with Room() as room: print(room)
执行之后会印出以下结果:
进入房间开灯在房间里关灯离开房间
这非常的直觉,当我们宣告 with Room()
时,就会自动去呼叫 Room()
里面的 __enter__()
这个函数。后面的 room
就是回传的字串 "在房间里" 哦!
当程式离开 with
的範围时,就会自动去呼叫 __exit__()
这个函数。这多了三个参数分别是 exc_type
(例外型态), exc_value
(例外值), traceback
(错误追蹤结果),下一个例子示範如何使用这三个参数。
class Room(): def turn_on_light(self): print("开灯") def turn_off_light(self): print("关灯") def __enter__(self): print("进入房间") self.turn_on_light() return "在房间里" def __exit__(self, exc_type, exc_value, traceback): if not exc_type: self.turn_off_light() print("离开房间") else: print("Exception type:", exc_type) print("Exception value:", exc_value) print("Traceback:", traceback)with Room() as room: print(i) # NameError
执行结果:
进入房间开灯Exception type: <class 'NameError'>Exception value: name 'i' is not definedTraceback: <traceback object at 0x0000023862DD4700>Traceback (most recent call last): File "D:\Andy\Code\test_with\test02.py", line 26, in <module> print(i)NameError: name 'i' is not defined. Did you mean: 'id'?
我故意使用一个没宣告过的变数,让程式跳出 NameError
,然后就会直接呼叫 __exit__()
,并传入错误讯息的参数。如果正常没出错的情况,exc_type
会是 None,就会去执行原本的程式 (关灯)。
所以 Python 常用的 open()
实际上就是一个 context manager,__enter__()
就写开档相关的程式,__exit__()
则写关档相关的程式,就完成啦!
偶尔去研究一下 Python 一些语法的实作也满有趣的呢?