python的with

with 语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的“清理”操作,释放资源。

比如文件使用后自动关闭、线程中锁的自动获取和释放等。

with open('test.txt', 'r') as f:
    for line in f:
        print(line)

运行机制

with VAR = EXPR:
    BLOCK

等价于

VAR = EXPR
VAR.__enter__()
try:
    BLOCK
finally:
    VAR.__exit__()

VAR对应一个上下文管理器(context manager)。

上下文管理器

实现了__enter__()和__exit__()两个方法的类就是一个上下文管理器。

class MyContextManager:
    def __enter__(self):
        print('before block run')
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('after block run')

使用上下文管理MyContextManager

with MyContextManager():
    print('run block')

输出如下:

before block run
run block
after block run

MyContextManager也可以接受参数

class MyContextManager:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __enter__(self):
        print('before block run')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('after block run')

    def show(self):
        print('my name is:', self.name)
        print('my age is:', self.age)

再次使用上下文管理MyContextManager

with MyContextManager('logan', 27) as myCM:
    myCM.show()

输出如下:

before block run
my name is: logan
my age is: 27
after block run

这里用到了一个as: with context_manager as target

target对应context_manager的__enter__()方法的返回值,返回值可以是context_manager自身,也可以是其他对象。

contextlib.contextmanager

 contextmanager可以对生成器函数进行装饰,返回结果是一个上下文管理器。

import contextlib

@contextlib.contextmanager
def MyGenerator():
    print('before yield')
    yield
    print('after yield')

with MyGenerator():
    print('run block')

输出如下:

before yield
run block
after yield

这里yield nothing,下面给个yield something的例子

import contextlib

@contextlib.contextmanager
def MyGenerator():
    print('before yield')
    yield 'context manager'
    print('after yield')

with MyGenerator() as cm:
    print('run block')
    print(cm)

输出如下:

before yield
run block
context manager
after yield

 

两个注意点

1. yield之前的语句类似于__enter__(),yield之后的语句类似于__exit__()方法。

2. yield返回一个上下文管理器,如果使用as语句,会被赋值给as语句中的target。

 

try...finally

当在try范围内产生一个异常时,会立即跳转到finally语句块。当finally语句块执行完毕后,会继续向上一层引发异常。

 

参考资料:

浅谈 Python 的 with 语句

 

posted @ 2017-08-08 21:44  Sawyer Ford  阅读(167)  评论(0编辑  收藏  举报