with和上下文管理器

shengge0 2020-02-12

with和上下文管理器

如果你有时间阅读源码的习惯,可能会看到一些优秀的代码会出现带有with关键字的语句。

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

在上篇文件中,我们读写文件操作,完毕之后,关闭文件对象。调用close方法,下面来看看如何正确的关闭一个文件。

普通版
def func():
    f = open("1.txt", "w")
    f.write("Python")
    f.close()

这样写有一个误区,如果在调用write方法时,出现了异常无法继续执行下面代码,资源就被该程序一直占用系统资源。

进阶版
def func():
    f = open("1.txt", "w")
    try:
        f.write("python")
    except IOError:
        print("oops, error")
    finally:
        f.close()

上面的程序我们使用异常进行了捕获,finally内代码执行关闭资源。如果需要学习异常的使用,请看下篇文章。

高级版
def func():
    with open("1.txt", "r") as f:
        f.write("python")

简洁优雅的写法就是使用with关键字。with的作用和使用异常捕获try/finally语句是一样的。想了解with原理,我们必须知道上下文管理器。

上下文管理器

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

自定义类实现上下文管理器,示例代码:

class File():
    def __init__(self, file_name, mode):
        self.file_name = file_name
        self.mode = mode
    
    def __enter__(self):
        self.f = open(self.file_name, self.mode)
        return self.f
    
    def __exit__(self, *args):
        self.f.close()

enter()方法返回资源对象,这就是要打开的文件对象,exit()方法处理清除工作。因为File类实现了上下文管理器,现在就可以使用with语句了。

with File("1.txt", "w") as f:
    f.write("hello, python")

这样一来,系统自动调用close方法实现关闭资源。

装饰器实现上下文管理器

Python提供了contextmanager的装饰器,更进一步简化了上下文管理器的实现方式,通过yield将函数分为两部分,yield之前的语句在__enter__()方法中执行,yield之后的语句在__exit__()执行。

from contextlib import contextmanger

@contextmanger
def my_open(path, mode):
    f = open(path, mode)
    yield f
    f.close()

调用:

with my_open("1.txt", "w") as f:
    f.write("hello, python")

本文中应用到了装饰器,异常,前面未讲到,如有需要,请看以后的文章,后续文章会讲到装饰器,下篇文章则是异常的使用。

相关推荐