第2章 上下文管理器

上下文管理器 是一个包装任意代码块的对象。上下文管理器保证进入上下文管理器时,每次代码执行的一致性;当退出上下文管理器时,相关资源会被正确收回。

  1. 关键字with

    with open("123.txt", "r")as f:
        contents = f.read()
        
    class File:
        
        def __init__(self, file):
            self.file = file 
            
        # 表示进入
        def __enter__(self):
            self.open_file = open(self.file, "r", encoding="utf-8")
            return self.open_file 
            
        # 表示退出
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.open_file.close()
            return True
            
    with File("123.txt") as file:
        print(file.read())
    
    • with语句的表达式的作用是返回一个遵循特定协议的对象,该对象必须定义一个__enter____exit__ 方法,且后者必须接受特定的参数
    class ContextManager:
    
        def __init__(self):
            self.entered = False
    
        def __enter__(self):
            self.entered = True
            return self
    
        # exc_type异常类型 exc_val异常实例  exc_tb回溯
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.entered = False
    
    con = ContextManager()
    
    print(con.entered)
    
    with con:  # 处于进入状态
        print(con.entered)
    with ContextManager() as cm:
        print(cm.entered)
    
  2. 适合编写上下文管理器

    # 资源清理
    import psycopg2     # postgreSQL
    class DBConnection:
        def __init__(self, dbname=None, user=None, password='5841', host='127.0.0.1'):
            self.host = host
            self.dbname = dbname
            self.user = user
            self.password = password
    
        def __enter__(self):
            self.connection = psycopg2.connect(
                dbname = self.dbname,
                host = self.host,
                user = self.user,
                password = self.password
            )
            return self.connection.cursor()
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.connection.close()
    #
    # with DBConnection(user='luke', dbname='foo')as db:
    #     db.execute('select 1 + 1')
    #     print(db.fetchall())
    
    
    # 避免重复
    class BubbleExceptions:
    
        def __enter__(self):
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_val:
                print("Bubbling up exception: %s."%exc_val)
            return False  # 返回值会给exc_tb 如果异常为假则重新抛出异常
        	# return True  异常为真时,不需抛出异常 回溯消失
    
    with BubbleExceptions():
        print(5/0)
        
    with BubbleExceptions():
        try:
            print(5/0)
        # 如果__exit__ 返回值为真时,不在执行下面语句
        except ZeroDivisionError as e:  
            print(e)
    
    # 处理异常类
    
    class HandleValuesError:
        def __enter__(self):
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            if not exc_type:
                return True
    
            if issubclass(exc_type, ValueError):
                print("Handling ValueError:%s"%exc_val)
                return True
    
            return  False
    
    with HandleValuesError():
        raise ValueError("wrong value.")
    
    # 不包括的子类
    class ValuesErrorSubclass(ValueError):
        pass
    
    class HandleValuesError:
        def __enter__(self):
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            if not exc_type:
                return True
    
            # print(issubclass(ValueError, ValueError))   # True
            if exc_type == ValueError:
                print("Handling ValueError:%s"%exc_val)
                return True
    
            return False
    
    with HandleValuesError():
        raise ValuesErrorSubclass("foo and baz")
    
    # https://www.runoob.com/w3cnote/python3-subprocess.html
    # 基于属性的异常处理
    import subprocess  # 进程处理模块
    
    class ShellException(Exception):
    
        def __init__(self, code, stdout="", stderr=""):
            self.code = code
            self.stdout = stdout
            self.stderr = stderr
    
        def __str__(self):
            return 'exit code %d - %s'%(self.code, self.stderr)
    
    class AcceptableErrorCodes:
        def __init__(self, *error_codes):
            self.error_codes = error_codes
    
        def __enter__(self):
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            if not exc_type:
                return True
            if not issubclass(exc_type, ShellException):
                return False
            return exc_val.code in self.error_codes
    
    
    def run_command(command):
        proc = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        proc.wait()
    
        stdout, stderr = proc.communicate()
    
        if proc.returncode > 0:
            raise ShellException(proc.returncode, stdout, stderr)
        return stdout
    
    with AcceptableErrorCodes():
        print(run_command("pip -list"))
    
  3. 更简单的语法

    
    import contextlib
    import subprocess  # 进程处理模块
    
    class ShellException(Exception):
    
        def __init__(self, code, stdout="", stderr=""):
            self.code = code
            self.stdout = stdout
            self.stderr = stderr
    
        def __str__(self):
            return 'exit code %d - %s'%(self.code, self.stderr)
    
    '''
    @contextlib.contextmanager 会将acceptable_error_codes转换成一个上下文管理器类
    该装饰器的函数在函数执行期间返回单个值
    '''
    @contextlib.contextmanager
    def acceptable_error_codes(*codes):
        try:
            yield
        except ShellException as exc_val:
            if exc_val.code not in codes:
                raise
            pass
    
    
    def run_command(command):
        proc = subprocess.Popen(command.split(' '), stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE)
        proc.wait()
    
        stdout, stderr = proc.communicate()
    
        if proc.returncode > 0:
            raise ShellException(proc.returncode, stdout, stderr)
        return stdout
    
    with acceptable_error_codes(1):
        print(run_command("rm 123.txt"))
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

壹如年少遲夏歸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值