python学习 - 上下文管理,with语句

参考:
https://www.cnblogs.com/flashBoxer/p/9664813.html
https://blog.csdn.net/qq_37482956/article/details/100056517

gluon的一个例子:https://mxnet.incubator.apache.org/api/python/docs/_modules/mxnet/autograd.html#is_training # _RecordingStateScope 函数

with的语法
with EXPR as VAR:
    BLOCK

上面的语法的伪代码

mgr = (EXPR)   
exit = type(mgr).__exit__  # Not calling it yet,还没有执行
value = type(mgr).__enter__(mgr)    
exc = True     
try:
    try:
        VAR = value  # Only if "as VAR" is present
        BLOCK
    except:
        # The exceptional case is handled here
        exc = False
        if not exit(mgr, *sys.exc_info()):
            raise
        # The exception is swallowed if exit() returns true
finally:
    # The normal and non-local-goto cases are handled here
    if exc:
        exit(mgr, None, None, None)

对上面的伪代码的一个解析:

  1. 生成上下文管理器mgr。
  2. 第2,3行会检查是否有 __exit__ 和 __enter__,没有的化,会抛出AttributeError异常。
  3. 将__enter__的值赋值给value;这个时候会调用__enter__并执行,__exit__还不会执行。
  4. 如果语法中没有写 as VAR,那么 VAR = value 就被忽略。
  5. 执行BLOCK内的代码:正常结束,或者是通过break, continue, return来结束;然后执行__exit__内的代码,__exit__的三个参数都是None,__exit__参数分别是:异常类型,异常值,追溯信息。
  6. 如果执行出现异常,执行except中的语句,调用__exit__时的参数就是sys.exc_info() #(exc_type, exc_value, exc_traceback)

注意:如果 __enter__ 没有返回值,那么 as 对象获取的值就是None

class WithTest:
    def __init__(self, filename):
        self.filename=filename
    def __enter__(self):
        self.f = open(self.filename, 'r')
       # return self.f
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()

test = WithTest('file')

with test as t:
    print ('test result: {}'.format(t))

输出结果:

test result: None

这个例子里面__enter__没有返回,所以with语句里的"as t"到的是None,修改一下上面的例子:

class WithTest:
    def __init__(self, filename):
        self.filename=filename
    def __enter__(self):
        self.f = open(self.filename, 'r')
        return self.f
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()

test = WithTest('file')

with test as t:
    print ('test result: {}'.format(t))

输出:

test result: <_io.TextIOWrapper name='skb.kc' mode='r' encoding='cp936'>

__enter__函数里有返回值赋值给as的对象t。

正常来讲,在BLOCK发生的错误会被传到__exit__的参数里,如果BLOCK代码没有发生错误,那么传入的三个参数就None,如果发生了错误,那么传入__exit__的参数就是:错误类型,错误值,和追溯信息,并且__exit__返回值为False的情况下才会触发 raise,抛出异常信息。

因此,如果在__exit__中返回True,即使BLOCK代码出现了错误,但仍然不会产生异常。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章