else 块 和上下文管理器

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 块 和上下文管理器)