美文网首页
else 块 和上下文管理器

else 块 和上下文管理器

作者: 刘烟火 | 来源:发表于2020-09-05 10:36 被阅读0次

    else 块

    我们知道在python 中最常用else的地方 就是 在if else 代码块中。而else 并不仅仅用于此,
    还可以用在 for , while , try 中

    
    # for else 
    def f():
        my_list = ['banana','apple']
        for item in my_list:
            if item == 'banana':
                #print(item) # 会进else 
                #return # 不会进else
                # a = 1/0 # 异常不会进else          
                break # 跳出循环 不会进else            
        else:
            # 当for循环 执行完就会走 这里 
            # (若 经过 break 或 return 跳出 了循环 则 不会进入else中)
            raise ValueError('No banana flavor found!')
        
    f()   
    
    
    try:
        dangerous_call()
    except OSError:
        log('OSError...')
    else:
        #当执行 dangerous_call不报错的情况下 ,才执行 after_call
        after_call() # else 块中出现异常  不会再进入 except
    

    在所有情况下,如果异常或者 return、break 或 continue 语句导致控制权跳到了复合语句的主块之外,else 子句也会被跳过。

    上下文

    上下文管理器对象存在的目的是管理 with 语句,就像迭代器的存在是为了管理 for 语句一样。
    with 语句的目的是简化 try/finally 模式。

    上下文管理器协议包含 enterexit 两个方法。with 语句开始运行时,会在上下文管理器对象上调用 enter 方法。with 语句运行结束后,会在上下文管理器对象上调用 exit 方法,以此扮演 finally 子句的角色

    如文件管理

    with open('1.txt') as fp:
        src = fp.read()
    
    print(fp)
    print(fp.closed)
    fp.read()
    

    报了一个错, “ValueError: I/O operation on closed file.”

    说明 这个文件对象 在with 块外,已经被关闭了, 这是因为在io.TextIOWrapper 的exit方法中关闭 文件。

    我们建一个类来说明 一下

    class LookingGlass:
        def __enter__(self):
            import sys
            self.original_write = sys.stdout.write
            sys.stdout.write = self.reverse_write #将系统的输入字符 反向输入
            return 'JABBERWOCKY'
        def reverse_write(self, text):
            self.original_write(text[::-1])
        def __exit__(self, exc_type, exc_value, traceback):
            import sys
            sys.stdout.write = self.original_write
            if exc_type is ZeroDivisionError:
                print('Please DO NOT divide by zero!')
                return True # 返回 True 代表  处理 了异常 , 不再往上冒泡异常
            
    with LookingGlass() as what:
        print('Alice, Kitty and Snowdrop')
        #     a = 3 + 's' # 会报 TypeError 异常
        a = 1/0 # with 语句块处理 除数0 异常  ,不会再向上抛
        print(what)
        
    print('Alice, Kitty and Snowdrop')
    print(what)
    

    打印结果:

    pordwonS dna yttiK ,ecilA
    YKCOWREBBAJ
    Alice, Kitty and Snowdrop
    JABBERWOCKY
    

    我们明显可以看到 在with块语句内是反向输入,外面则是正向输入,需要注意的是 exit 方法返回 None,或者 True 之外的值,with 块中的任何异常都会向上冒泡

    使用@contextmanager

    @contextmanager 装饰器能减少创建上下文管理器的样板代码量,因为不用编写一个完整的类,定义 enterexit 方法,而只需实现有一个 yield 语句的生成器,生成想让 enter 方法返回的值。
    在使用 @contextmanager 装饰的生成器中,yield 语句的作用是把函数的定义体分成两部分:yield 语句前面的所有代码在 with 块开始时(即解释器调用 enter 方法时)执行, yield 语句后面的代码在with 块结束时(即调用 exit 方法时)执行。

    import contextlib
    
    @contextlib.contextmanager
    def looking_glass():
       import sys
       original_write = sys.stdout.write
       
       def reverse_write(text):
           original_write(text[::-1])
    
       sys.stdout.write = reverse_write
       msg = ''
       try:
           yield '你好世界'
       except ZeroDivisionError:
           msg = 'Please DO NOT divide by zero!'
       finally:
           sys.stdout.write = original_write
           if msg:
               print(msg)
              
               
    with looking_glass() as what:
       print('abc')
    #     a = 1 + 's'
       a = 1/0
       print('world')   
    print('abc')
    print('world')  
    

    前面说过,为了告诉解释器异常已经处理了,exit 方法会返回True,此时解释器会压制异常。如果 exit 方法没有显式返回一个值,那么解释器得到的是 None,然后向上冒泡异常。使用@contextmanager 装饰器时,默认的行为是相反的:装饰器提供的exit 方法假定发给生成器的所捕获的异常都得到处理了,因此应该压制异常。 如果不想让 @contextmanager 压制异常,必须在被装饰的函数中显式重新抛出异常。

    相关文章

      网友评论

          本文标题:else 块 和上下文管理器

          本文链接:https://www.haomeiwen.com/subject/lsixektx.html