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 模式。
上下文管理器协议包含 enter 和 exit 两个方法。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 装饰器能减少创建上下文管理器的样板代码量,因为不用编写一个完整的类,定义 enter 和 exit 方法,而只需实现有一个 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 压制异常,必须在被装饰的函数中显式重新抛出异常。
网友评论