python实现with上下文管理,__exit__()参数exc_type, exc_val, exc_tb异常处理

自己写一下Python的with语句实现

第一种方法利用魔法方法__enter__和__exit__实现:

顾名思义__enter__是打开上文,__exit__关闭下文,相当于打开文件、关闭文件。

#coding=gbk
#第一种,魔法方法实现方法
class WithContext():
    def __init__(self,name=None):
        print('接收参数:%r'%name)
        self.name=name
    def with_context(self):
        print(self.name)
        print('调用with方法')
    def __enter__(self):
        print('调用enter方法')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('调用exit方法')


if __name__ == '__main__':
    # 'test_with'作为参数传递给__init__方法
    #w为__enter__方法的返回值
    with WithContext('test_with') as w:
        print('with语句返回值:%r'%w)
        print('with语句返回值类型:%r'%type(w))
        print(w.name)
        w.with_context()

 执行结果:

接收参数:'test_with'
调用enter方法
with语句返回值:<__main__.WithContext object at 0x00000000010B98D0>
with语句返回值类型:<class '__main__.WithContext'>
test_with
test_with
调用with方法
调用exit方法

'test_with'作为参数传递给__init__方法
w为__enter__方法的返回值

可以看出各个方法的执行顺序:

__init__

__exit__

with_context

__exit__

 with语句中的__enter__无返回值的情况:

#coding=gbk
#第一种,魔法方法实现方法
class WithContext(object):
    def __new__(cls,*args,**kwargs):
        if not hasattr(cls,'_instance'):
            cls._instance=super().__new__(cls)
        cls._instance.name='test_with'
        return cls._instance
    def __init__(self,name=None):
        self.name=name
        print('接收参数:%r'%name)
    def with_context(self):
        print(self.name)
        print('调用with方法')
    def __enter__(self):
        print('调用enter方法')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('调用exit方法')


if __name__ == '__main__':
    # 'test_with'作为参数传递给__init__方法
    #w为__enter__方法的返回值
    with WithContext('test_with') as w:
        print('with语句返回值:%r'%w)
        print('with语句返回值类型:%r'%type(w))







运行结果: 

接收参数:'test_with'
调用enter方法
with语句返回值:None
with语句返回值类型:<class 'NoneType'>
调用exit方法

顺便说下__exit__方法中的这个三个参数‘exc_type, exc_val, exc_tb’,

异常类型,异常值和异常的trackback

如果with语发生异常,但不希望抛出,__exit__方法应该返回return True,相当于with语句自动做一个try,except处理,

class WithContext(object):

    def __init__(self,name=None):
        self.name=name
        print('接收参数:%r'%name)
    def with_context(self):
        print(self.name)
        print('调用with方法')
    def __enter__(self):
        print('调用enter方法')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type, exc_val, exc_tb)
        print('调用exit方法')
        return True
        #return True


if __name__ == '__main__':
    # 'test_with'作为参数传递给__init__方法
    #w为__enter__方法的返回值
    with WithContext('test_with') as w:
        #发生异常
        a=1/0
        #a=1/0
        pass
        

执行结果:

虽然发生异常,但是并没有报错。

接收参数:'test_with'
调用enter方法
<class 'ZeroDivisionError'> division by zero <traceback object at 0x0000000000BAE548>
调用exit方法

如果把return True注释掉,

结果如下:

抛出了异常,打印出了trackback

Traceback (most recent call last):
接收参数:'test_with'
调用enter方法
  File "C:/Users/wangjinyu/PycharmProjects/work_practice/p_103_with_context.py", line 49, in <module>
<class 'ZeroDivisionError'> division by zero <traceback object at 0x000000000081E548>
    a=1/0
调用exit方法
ZeroDivisionError: division by zero

如果把a=1/0注释掉,换成pass,exc_type, exc_val, exc_tb的值均为None

输出结果:

接收参数:'test_with'
调用enter方法
None None None
调用exit方法

 

参考:

官方文档:

https://docs.python.org/3/reference/datamodel.html#object.__exit__

object.__exit__(selfexc_typeexc_valuetraceback)

Exit the runtime context related to this object. The parameters describe the exception that caused the context to be exited. If the context was exited without an exception, all three arguments will be None.

If an exception is supplied, and the method wishes to suppress the exception (i.e., prevent it from being propagated), it should return a true value. Otherwise, the exception will be processed normally upon exit from this method.

Note that __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.

 

 blog:http://wsfdl.com/python/2013/09/08/python_with.html

 

第二种方法,用装饰器实现,百度后简单试了下,其实就是利用yield关键字的生成器实现

import contextlib

@contextlib.contextmanager
def with_context(a):
    print('执行前')
    res=a*a
    yield res
    print('执行后')


if __name__ == '__main__':
    #f就是yield 的返回值res
    #参数2实参,传递给形参a
    with with_context(2) as f:
        print(f*f)

执行结果: 

执行前
16
执行后

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值