当前位置 : 主页 > 编程语言 > python >

Python with与上下文管理器

来源:互联网 收集:自由互联 发布时间:2022-07-05
仅供学习,转载请注明出处 with与“上下文管理器” 如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有 “with” 关键字的语句,它通常用在什么场景呢? 对于系统资源

仅供学习,转载请注明出处

with与“上下文管理器”

如果你有阅读源码的习惯,可能会看到一些优秀的代码经常出现带有 “with” 关键字的语句,它通常用在什么场景呢?

对于系统资源如文件、数据库连接、socket 而言,应用程序打开这些资源并执行完业务逻辑之后,必须做的一件事就是要关闭(断开)该资源。

比如 Python 程序打开一个文件,往文件中写内容,写完之后,就要关闭该文件,否则会出现什么情况呢?极端情况下会出现 "Too many open files" 的错误,因为系统允许你打开的最大文件数量是有限的。

同样,对于数据库,如果连接数过多而没有及时关闭的话,就可能会出现 "Can not connect to MySQL server Too many connections",因为数据库连接是一种非常昂贵的资源,不可能无限制的被创建。

来看看如何正确关闭一个文件。

普通版:

def file1():
f = open("fatboss.txt","w")
f.write("胖子老板:买包槟榔啦,小伙子")
f.close()

if __name__ == "__main__":
file1()

执行如下:

[root@server01 with]# ls
test1.py
[root@server01 with]#
[root@server01 with]# python3 test1.py
[root@server01 with]#
[root@server01 with]# ls
fatboss.txt test1.py
[root@server01 with]# cat fatboss.txt
胖子老板:买包槟榔啦,小伙子[root@server01 with]#
[root@server01 with]#

这种方式比较繁琐,就是每次在写入file结束之后,手动去关闭。但是如果写入过程出现异常,那么就会报错终止整个python进程。

那么为了解决这个异常的问题,可以增加 try 捕获异常的机制。

捕获异常版

def file1():
try:
f = open("fatboss.txt","w")
except IOError:
print("打开文件出现异常啦!")
else:
f.write("胖子老板:买包芙蓉王啦,小伙子")
finally:
f.close()

if __name__ == "__main__":
file1()

程序是对可能发生异常的代码处进行 try 捕获,使用 try/finally 语句,该语句表示如果在 try 代码块中程序出现了异常,后续代码就不再执行,而直接跳转到 except 代码块。如果没有出现异常,则跳转到else代码块执行。而无论如何,finally 块的代码最终都会被执行。因此,只要把 close 放在 finally 代码中,文件就一定会关闭。

运行如下:

[root@server01 with]# python3 test1.py
[root@server01 with]# cat fatboss.txt
胖子老板:买包芙蓉王啦,小伙子[root@server01 with]#
[root@server01 with]#

但是如果这样的写法的话,代码就要写很多了。有没有简便的方法呢?
下面来介绍一下 ​​​with​​ 关键字的用法。

​​with​​ 简洁写法

def file1():

with open("fatboss.txt","w") as f:
f.write("胖子老板:买包蓝利群啦,小伙子")

if __name__ == "__main__":
file1()

运行如下:

[root@server01 with]# python3 test1.py
[root@server01 with]#
[root@server01 with]# cat fatboss.txt
胖子老板:买包蓝利群啦,小伙子[root@server01 with]#

with 关键字 后面的 open 方法的返回值赋值给变量 f,当离开 with 代码块的时候,系统会自动调用 f.close() 方法, with 的作用和使用 try/finally 语句是一样的。那么它的实现原理是什么?在讲 with 的原理前要涉及到另外一个概念,就是上下文管理器(Context Manager)。

什么是上下文(context)

上下文在不同的地方表示不同的含义,要感性理解。

例如:胖子老板一开口喊住了我,那么这就是上文。

下文是什么呢?

当然就是买烟、买可乐、买槟榔啦!!! 这就是下文啦。


Python with与上下文管理器_数据库连接

哦,上下文就是这样? Python with与上下文管理器_代码块_02

不然呢

上下文管理器

任何实现了 ​​__enter__()​​​ 和 ​​__exit__()​​ 方法的对象都可称之为上下文管理器,上下文管理器对象可以使用 ​​with​​​ 关键字。显然,文件​​(file)​​对象也实现了上下文管理器。

那么文件对象是如何实现这两个方法的呢?我们可以模拟实现一个自己的文件类,让该类实现 ​​__enter__()​​​ 和 ​​__exit__()​​ 方法。

class File():

def __init__(self, filename, mode):
self.filename = filename
self.mode = mode

def __enter__(self):
print("entering")
self.f = open(self.filename, self.mode)
return self.f

def __exit__(self, *args):
print("exitting")
self.f.close()

def main():
# 使用自定义的File类来执行一下
with File("fatboss2.txt","w") as f:
f.write("胖子老板:槟榔、芙蓉来一套!")

if __name__ == "__main__":
main()

运行如下:

[root@server01 with]# python3 test2.py
entering
exitting
[root@server01 with]# ls
fatboss2.txt fatboss.txt test1.py test2.py
[root@server01 with]# cat fatboss
cat: fatboss: No such file or directory
[root@server01 with]# cat fatboss2.txt
胖子老板:槟榔、芙蓉来一套![root@server01 with]#
[root@server01 with]#

​​__enter__()​​​ 方法返回资源对象,这里就是你将要打开的那个文件对象,​​__exit__()​​方法处理一些清除工作。

因为 File 类实现了上下文管理器,现在就可以使用 with 语句了。

使用​​@contextmanager​​实现上下文管理器的另外方式

Python 还提供了一个 ​​contextmanager​​​ 的装饰器,更进一步简化了上下文管理器的实现方式。
通过 ​​​yield​​​ 将函数分割成两部分,​​yield​​​ 之前的语句在 ​​__enter__​​​ 方法中执行,​​yield​​​ 之后的语句在 ​​__exit__​​​ 方法中执行。紧跟在 ​​yield​​ 后面的值是函数的返回值。

from contextlib import contextmanager

@contextmanager
def my_open(path,mode):
f = open(path,mode) ## __enter__
yield f
f.close() ## __exit__

def main():
# 使用自定义的File类来执行一下
with my_open("fatboss2.txt","w") as f:
f.write("胖子老板:槟榔、芙蓉、蓝利群来一套!\n")

if __name__ == "__main__":
main()

运行如下:

[root@server01 with]# python3 test2.py
[root@server01 with]#
[root@server01 with]# cat fatboss2.txt
胖子老板:槟榔、芙蓉、蓝利群来一套!
[root@server01 with]#

总结

Python 提供了 with 语法用于简化资源操作的后续清除操作,是 try/finally 的替代方法,实现原理建立在上下文管理器之上。此外,Python 还提供了一个 contextmanager 装饰器,更进一步简化上下管理器的实现方式。


Python with与上下文管理器_python_03


关注微信公众号,回复【资料】、Python、PHP、JAVA、web,则可获得Python、PHP、JAVA、前端等视频资料。


上一篇:Python 继承
下一篇:没有了
网友评论