KbMan

操作文件对象时可以:

with open(\'a.txt\') as f:
    \'代码块\'

上述叫做上下文管理协议,即with语句。

想象一下,你有两个需要结对执行的相关操作,然后,还要在他们中间放置一段代码。比如打开一个文件,操作文件,然后关闭该文件。

打开文件和关闭文件就是一个结对的操作。

上下文管理器的常见用例:是资源的加锁与解锁,文件的打开与关闭。

上下文管理器

上下文管理器协议:是指类需要实现 __ enter __ 和 __ exit __ 方法。

就跟迭代器有迭代器协议一样,迭代器协议需要实现 __ iter __ 和 __ next __ 方法。

上下文管理器,也就是支持上下文管理协议的对象,简单点讲就是,实现了 __ enter __ 和 __ exit __两个方法的类。这个类也叫做,上下文管理器的类。

写一个Open类,这个类是一个上下文管理器:

class Open:
    def __init__(self, filepath, encoding):
        self.filepath = filepath
        self.encoding = encoding

    def __enter__(self): # 当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量
        print(\'当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量\')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(\'with 中代码块执行完就执行我这个函数\')


with Open(\'1.txt\', \'UTF-8\') as f:
    print(\'with 里面的代码块\')
\'\'\'
结果:
当这个类被with关键字执行时,就自动调用这个方法。有返回值则调用给 as 声明的变量
with 里面的代码块
with 中代码块执行完就执行我这个函数
\'\'\' 

__ exit __(self, exc_type, exc_val, exc_tb):

里面的三个参数分别代表:异常类型,异常值,追溯信息。

注意:with语句中的代码块出现异常后,with后的代码都无法执行

基于类的实现:完整实现Open方法

一个上下文管理器的类,起码要定义 __ enter __ 和 __ exit __ 方法。

class Open:
    def __init__(self, filepath, method):
        self.file = open(filepath, method, encoding=\'utf-8\')

    def __enter__(self):
        return self.file

    def __exit__(self, type, value, traceback):
        self.file.close()


with Open(\'1.txt\', \'w\') as f:
    f.write(\'1111111111\')

我们来看看底层发生了什么?

  1. with语句先暂存了 Open 类的 __ exit __ 方法
  2. 然后调用 Open 类的 __ enter __ 方法
  3. __ enter __ 方法打开文件并返回给with语句
  4. 打开的文件句柄传递给 as 后面的 f 参数
  5. 执行with里面的代码块。
  6. 调用之前暂存的 __ exit __ 方法
  7. 关闭文件

在第4步和第6步之间,如果发生异常,Python会将异常的type,value,traceback传递给 __ exit __ 方法。

当异常发生时,with语句会采取哪些步骤?

  1. with把异常的type, value, traceback 传递给 __ exit __ 方法
  2. with让 __ exit __ 处理异常
  3. 如果 __ exit __ 返回的是True, 那么这个异常就被优雅的处理了。
  4. 如果 __ exit __ 返回的是True以外的任何东西,那个这个异常将被with 语句抛出。

当 __ exit __()返回值为True, 那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行.。

完整模拟Open:

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

    def __enter__(self):
        self.file = open(self.filepath, mode=self.mode, encoding=self.encoding)
        return self.file

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        self.file.close()
        return True


with Open(\'1.txt\', \'w\', encoding=\'utf-8\') as f:
    f.write(\'哈哈哈\')
    f.werwer # 抛出异常,交给exit处理。后面的代码正常运行

优点

  1. 使用with的语句的目的就是把代码块放入with中执行, with结束后,自动完成清理工作,无需干预。
  2. 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在 __ exit __ 中定制自动释放资源的机制。

基于生成器实现一个上下文管理器

contextlib模块:可以使用一个生成器实现一个上下文管理器,而不是使用一个类。众所周知,在类中还需要实现 __ enter __ 和 __ exit __ 。

from contextlib import contextmanager

@contextmanager
def point(x, y):
    print(\'在yield之前\')

    yield x * y  # yield出去的值赋给 as 后面的变量

    print(\'在yield之后\')


with point(3, 4) as p:
    print(\'p\',p)
    
    
\'\'\'
结果:
在yield之前
p 12
在yield之后
\'\'\'

利用contextlib模块实现一个open

@contextmanager
def my_open(path):

    f = open(path, mode=\'w\')

    yield f  # 把这个f 赋给as后面的变量

    f.close()

with my_open(\'2.txt\') as f:

    f.write(\'我是你爹\')

分类:

技术点:

相关文章: