简单认识下with和上下文管理器

with

对于系统资源如文件、数据库连接、socket,应用程序打开这些资源并执行完业务逻辑之后,必须关闭(断开)该资源。系统允许打开的最大文件数量是有限的,如果我们打开文件后没有及时关闭,极端情况下会出现:Too many open files 错误。对于数据库来说,如果没有关闭的连接数量过多,也会出现:Can not connect to MySQL server Too many connections 错误。

我们最开始最基础使用的是以下方式来关闭资源

def m1():
    f = open("output.txt", "w")
    f.write("python之禅")
    f.close()
    print("查看文件是否已关闭:",f.closed)

m1()
    
# 结果
# 查看文件是否已关闭: True

在这里插入图片描述

在能够确保程序稳定安全运行的情况下,这样运行没有问题。一旦代码量增多,可能会忘记使用close去关闭文件,而且最大的问题是,如果程序运行在中间代码如f.write(“python之禅”)时出错,那后面就不再运行f.close(),文件也就无法正常关闭。针对第二个问题,我们采用了try…exception…finally…的方式:

def m1():
    try:
        f = open("output.txt", "w")
        f.write("hello world!")
        print("查看文件是否已关闭:", f.closed)
    except Exception as e:
        print(e)
    finally:
        f.close()
        print("查看文件是否已关闭:",f.closed)
        
m1()

# 结果
# 查看文件是否已关闭: False
# 查看文件是否已关闭: True

在这里插入图片描述

我们都知道,不管程序在运行try语句中出现了什么问题,都会运行finally中的语句。现在虽然解决了第二个问题,那有没有什么办法能够解决第一个问题呢?这个时候就要提及with了,我们先看看下面一段代码:

def m1():
    with open("output.txt", "w") as f:
        f.write("python之禅")
    print("查看文件是否已关闭:",f.closed)

m1()

# 结果
# 查看文件是否已关闭: True

在这里插入图片描述

上面的代码中,我们只是使用了一个with,并没有看到使用了close(),代码运行完之后,文件就关闭了。实际上它并不是没有运行到close(),而是在with内部语句运行完之后,自动调用了文件类中的__exit__()魔法方法,在这个方法中实现了f.close(),整块代码运行过程如下:

1、运行m1(),调用m1变量指向的代码块

2、运行with open(“output.txt”, “w”) as f,使用关键字with,开启上下文管理,f = open(“output.txt”, “w”),即自动调用文件类中的__enter__()魔法方法,得到self.f

3、运行f.write(“python之禅”)

4、跳出with前,自动运行__exit__()魔法函数,实现self.f.close()关闭文件

5、打印出文件关闭状态

看完上面的流程,想必对with和两个魔法方法、上下文管理器还是一头雾水。那我们就再了解下什么是上下文管理器,with、enter()、exit()和上下文的关系。

上下文管理器

所谓上下文管理器,就是实现了__enter__()和__exit__()两个魔法方法的对象,上下文管理器可以使用with关键字。在遇到with关键字的时候,会自动去调用运行需要处理的对象中的__enter__()魔法方法,在运行完with中的语句时,会自动去调用运行处理完的对象的中的__exit__()魔法方法,前后呼应。

上面的例子中,文件之所以能够使用with,并在使用后自动关闭文件,就是因为文件对象实现了__enter__()魔法方法和__exit__()魔法方法。下面我们来证实下:

class MyFile:
    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):
        self.f.close()
        print("exit......")

with MyFile("output.txt", "w") as f:
    print("writing")
    f.write("hello, world!")
    
# 结果
# entering.......
# writing
# exit......

在这里插入图片描述

contextManager

除了通过__enter__()和__exit__()方法实现上下文管理器,还有一种更加简便的方式,就是装饰器+yield

from contextlib import contextmanager


@contextmanager
def my_test(filename, mode):
    f = open(filename, mode)
    yield f
    f.close()

with my_test("output.txt", "w") as f:
    f.write("hello, dear!")

在这里插入图片描述

当执行f = my_test(“output.txt”, “w”)时,会执行mytest()中的 f = open(filename, mode)语句,遇到yield时,返回f并停止运行。当运行完with中的代码时,猜测是调用了next()方法继续运行mytest上次中断后的代码,即f.close()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值