loadlj / blog

19 stars 6 forks source link

Python 中的Context Managers #8

Open loadlj opened 7 years ago

loadlj commented 7 years ago

Python 中的Context Managers

with语法

在Pyhton中最常见的with语法打开一个文件:

with open('context_managers.txt', 'r') as infile:
    print infile

不使用with:

infile = open('context_managers.txt', 'r')
print infile
infile.close()

with的作用就是不需要你去调用close方法,它会自动帮你去调用close()。比如我们不使用with的时候去操作文件io时可能会碰到一些异常,我们就需要使用try...except...finally这种复杂的方式去处理异常:

try:
    infile = open('context_managers.txt', 'r')
    raise Exception("some unknown error occur")
except Exception, e:
    pass
finally:
    infile.close()

with就相当于是上下文管理器(context managers), 调用with的时候就相当于声明了一个函数。它包含两个方法__enter__()__exit__(), 当我们调用with xxx as xxx的时候就相当于调用__enter__()打开了一个文件描述符,当整个with函数结束的时候,调用__exit__()方法去关闭文件描述符。

class file_open():

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

    def __enter__(self):
        self.open_file = open(self.filename, self.mode)
        return self.open_file

    def __exit__(self, *args):
        self.open_file.close()

with file_open('foo.txt', 'w') as infile:
    pass

contextlib库

contextlib是用来操作上下文管理器的,可以使用一个简单的语法@contextmanager去声明一个上下文管理器,使用时必须要带有yield关键字。这里的yield就相当于上面提到的__enter__()__exit__()分界线,在yield之前的部分都可以认为是__enter___()里边的代码,在其之后的都认为是__exit__()方法。

from contextlib import contextmanager

@contextmanager
def file_open(file_name, mode):
    open_file = open(file_name, mode)
    yield open_file
    open_file.close()

with file_open('foo.txt', 'w') as infile:
    pass

python的官方文档也有一个例子:

from contextlib import contextmanager

@contextmanager
def tag(name):
    print("<%s>" % name)
    yield
    print("</%s>" % name)

with tag("test"):
    print("foo")

output:

<test>
foo
</test>

参考来源