上下文管理器

定义

任何实现了上下文协议(也就是实现了__enter__和__exit__方法)的对象即为上下文管理器。所以也可以认为上下文管理器是一个包装任意代码块的对象。

使用场景及常用的上下文管理器

事实上上下文管理器的任务是 – 代码块执行前准备,代码块执行后收拾。常见用with关键字去实现上下文管理器,常用于文件处理上面。上下文管理器常常用于异常处理

好处

在写代码时,我们希望把一些操作放到一个代码块中,这样在代码块中执行时就可以保持在某种运行状态,而当离开该代码块时就执行另一个操作,结束当前状态。

举一个最浅显的例子,用open打开文件之后需要手动的去closed文件,但是如果是使用with关键字打开文件(相当于是使用的上下文管理器去打开的一个上下文对象),就不需要我们手动的去closed文件,因为上下文管理器会帮我们去处理这些后续工作。也可以认为上下文管理器是确保资源正确清理的一种方式。

上下文管理器的目的就是规定对象的使用范围,如果超出范围就采取“处理”,这一功能是在Python3.5之后引进的,它的优势在于可以使得你的代码更具可读性,且不容易出错。

简单来说,也就是上下文管理器能更好地实现对资源的自动创建与自动释放,允许你在有需要的时候,精确地分配和释放资源。

自定义的上下文管理器及应用

实现上下文管理器可以有两种方式,一种为基于类的实现,一种为基于生成器的实现

上下文管理器的实现

基于类的实现

基于类的实现需要实现两个方法:enter()和_exit_()
enter():负责进入代码块的准备工作,进入前被调用;
exit():负责离开代码块的善后工作,离开后被调用;
用于异常捕获,让程序能正常运行下去:

基于类实现上下文管理器:

class BubbleException(object):
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_val:
            print(f"Bubbling up excetion: {exc_val}")
        return False
with BubbleException():
    print(1+1)
with BubbleException():
    print(1/0)
print("发生了异常") 

运行截图:
在这里插入图片描述
因为产生了异常,所以该语句不能正常执行。但是将___exit__里面的return值改为true,则就能正常运行。即就是将异常截取,不让其传播

class BubbleException(object):
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_val:
            print(f"Bubbling up excetion: {exc_val}")
        return True
with BubbleException():
    print(1+1)
with BubbleException():
    print(1/0)
print("发生了异常") 

在这里插入图片描述
2.希望只捕获到当前的异常,不捕获其子类的异常

####4.上下文管理器捕获一个异常,但是不希望捕获它的子类异常
class valueerrorsub(ValueError):
    pass
class HandleValueError():
    def __init__(self):
        self.name="hxy"

    def __enter__(self):
        return self   #有实例对象的时候一定要返回本身(as name),否则name对象不是一个对象。

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not exc_type:
            return True

        if exc_type==ValueError:
        #if issubclass(exc_type,ValueError):  ###判断exc_type是不是Valueerror或者是其子类
            print(f"Handling ValueError:{exc_val}")
            return True
        ##让其他错误传播下去
        return False
with HandleValueError() as h:
    print(h.name)
    raise ValueError("value error")
with HandleValueError() as b:
    print(b.name)

运行截图:
在这里插入图片描述
此时抛出一个子类异常:

class valueerrorsub(ValueError):
    pass
class HandleValueError():
    def __init__(self):
        self.name="hxy"

    def __enter__(self):
        return self   #有实例对象的时候一定要返回本身(as name),否则name对象不是一个对象。

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not exc_type:
            return True

        if exc_type==ValueError:
        #if issubclass(exc_type,ValueError):  ###判断exc_type是不是Valueerror或者是其子类
            print(f"Handling ValueError:{exc_val}")
            return True
        ##让其他错误传播下去
        return False
with HandleValueError() as h:
    print(h.name)
    raise ValueError("value error")
with HandleValueError() as b:
    print(b.name)
    raise valueerrorsub("valuesub error")

运行结果:
在这里插入图片描述
可以看到子类异常也传播出去了

基于生成器的实现:

contextlib模块专门用于这个目的
用contextlib模块+生成器函数yield去自定义一个上下文管理器

##3用contextlib去实现上下文管理器
import contextlib
@contextlib.contextmanager   ###使用contecxtlib加生成器去实现上下文管理器
def context():
    print("entering the zone")    #yield之前的内容相当于enter里面的内容。
    try:
        yield
    except Exception as e:   #yield之后就是exit里面的内容
        print(f"with error {e}")

    else:
        print("with no error")

with context():
    print("use context")
    raise ValueError("asdg")    ####强制抛出异常

运行结果:
在这里插入图片描述

自定义上下文管理器和自定义shell异常类的运用

###编写一个shellException异常,返回状态码及错误详情
###编写run_command函数,实现运行shell命令功能,返回stdout
##编写上下文管理器,处理指定类型的错误码,如错误码为1则忽略次错误

from subprocess import Popen, PIPE, STDOUT
import os


def run_command(command):
    currentPath = os.path.dirname(os.path.realpath(__file__))
    p = Popen(command, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True, cwd=currentPath)
    output, errors = p.communicate()
    resultcode=p.returncode   ##得到执行命令的状态码
    context=bytes(errors).decode("utf-8")    ###得到错误详情
    result=bytes(output).decode("utf-8")    ###得到执行的正确结果
    if resultcode !=0:     ###如果有错误,则抛出自定义异常
        raise shellException(resultcode,context)
    return result   ###返回执行命令的结果

###shellException异常
class shellException(Exception):
    def __init__(self,code,content):
        self.code=code
        self.content=content
    def get_error_info(self):
        return [self.code,self.content]

###上下文管理器
class shellContext():
    def __init__(self,command):
        self.command=command

    def __enter__(self):
        return self

    def run_command(self):
        self.result=run_command(self.command)
        print("执行结果为:\n",self.result)

    def __exit__(self, exc_type, exc_val, exc_tb):
        print(exc_type)
        if exc_type:     ##捕捉到异常
            print("已经捕捉到异常")
            if exc_val.code!=1:
                print("错误详情:",exc_val.content)
                return True
            return False
command=input("请输入你的操作:")
with shellContext(command) as f:
    f.run_command() 

运行截图(由于使用了linux命令,因此需要在linux环境下运行):
在这里插入图片描述

参考链接

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值