0%

python进阶之上下文管理器

一、文管理器作用

上下⽂管理器允许你在有需要的时候,精确地分配和释放资源。

二、案例介绍:

对它使⽤最⼴泛的案例就是with语句了。想象下当你有两个相关操作,你想让它们结对执⾏,然后在它们俩中间放置⼀段代码。

上下⽂管理器就是专门让你做这个事情的。举个例⼦:

1
2
3

with open('some_file', 'w') as opened_file:
opened_file.write('Hola!')

上⾯这段代码打开了⼀个⽂件,往它⾥⾯写⼊了⼀些数据,然后关闭了它。如果在往⽂件

写数据时发⽣异常,它会尝试去关闭⽂件。上⾯那段代码与这⼀段是等价的:

1
2
3
4
5
6
7

file = open('some_file', 'w')
try:

file.write('Hola!')
finally:
file.close()

对比两个功能一样的代码,相比较之下,with 代码更简洁,去掉了很多样板代码,它确保我们的⽂件会被关闭。

三、使用上下文管理器with的优点:

(1)使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预。

(2)在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在exit中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处。

四、如何上下文管理器

1、基于类的实现

⼀个上下⽂管理器的类,最起码要定义enterexit⽅法。

让我们来构造我们⾃⼰的⽂件开启的上下⽂管理器,并学习下基础知识。

1
2
3
4
5
6
7
8

class File(object):
def __init__(self, file_name, method):
self.file_obj = open(file_name, method)
def __enter__(self):
return self.file_obj
def __exit__(self, type, value, traceback):
self.file_obj.close()

通过定义enterexit⽅法,我们可以在with语句⾥使⽤它。我们来试试:

1
2
3

with File('demo.txt', 'w') as opened_file:
opened_file.write('Hola!')

我们的exit函数接受三个参数。这些参数对于每个上下⽂管理器类中的exit

⽅法都是必须的。我们来谈谈在底层都发⽣了什么。

  1. with语句先暂存了File类的exit⽅法

  2. 然后它调⽤File类的enter⽅法

  3. enter⽅法打开⽂件并返回给with语句

  4. 打开的⽂件句柄被传递给opened_file参数

  5. 我们使⽤.write()来写⽂件

  6. with语句调⽤之前暂存的exit⽅法

  7. exit⽅法关闭了⽂件

(**1) 如何处理异常**

我们还没有谈到exit⽅法的这三个参数:type, value和traceback。

在第4步和第6步之间,如果发⽣异常,Python会将异常的type,value和traceback传递

exit⽅法。

它让exit⽅法来决定如何关闭⽂件以及是否需要其他步骤。在我们的案例中,我们

并没有注意它们。

那如果我们的⽂件对象抛出⼀个异常呢?万⼀我们尝试访问⽂件对象的⼀个不⽀持的⽅

法。举个例⼦:

1
2
3

with File('demo.txt', 'w') as opened_file:
opened_file.undefined_function('Hola!')

我们来列⼀下,当异常发⽣时,with语句会采取哪些步骤。

  1. 它把异常的type,value和traceback传递给exit⽅法

  2. 它让exit⽅法来处理异常

  3. 如果exit返回的是True,那么这个异常就被优雅地处理了。

  4. 如果exit返回的是True以外的任何东西,那么这个异常将被with语句抛出

2)怎么优雅的去处理异常:

看到处理异常那里,我们仅仅就是在发生异常时,文件能正常被关闭,不需要抛出异常

1
2
3
4
5

def __exit__(self, type, value, traceback):
print("Exception has been handled")
self.file_obj.close()
return True

我们仅仅return True,这么做就可以了

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

class Open:
def __init__(self, filepath, mode='r', encoding='utf-8'):
self.filepath = filepath
self.mode = mode
self.encoding = encoding

def __enter__(self):
# print('enter')
self.f = open(self.filepath, mode=self.mode, encoding=self.encoding)
return self.f

def __exit__(self, exc_type, exc_val, exc_tb):
# print('exit')
self.f.close()
return True
def __getattr__(self, item):

return getattr(self.f, item)

with Open('a.txt','w') as f:
print(f)
f.write('aaaaaa')
#f.wasdf() #抛出异常,交给__exit__处理