大家比较熟悉的else是与if搭配,如果if条件不成立,则执行else里的内容。但是并不是只有if才可以和else搭配,for,while和try都可以搭配else。
上下文管理器可以对上下文进行管理,上下文管理器中有两个必须的方法:__enter__和__exit__。当你使用上下文管理器对象调用__enter__方法时,就进入了上下文管理中,__enter__中的操作会对上下文进行影响,要停止上下文管理时,就调用__exit__方法停止上下文管理。
class UpperOut:
def __enter__(self):
self.original_write = sys.stdout.write
sys.stdout.write = self.upper_write
return "this is return"
def upper_write(self, word):
self.original_write(word.upper())
def __exit__(self, exc_type, exc_value, traceback):
sys.stdout.write = self.original_write
if exc_type is ZeroDivisionError:
print("Please do not divide by zero")
return True
Ipython 中调试结果如下:
>>> from test import UpperOut
>>> manager = UpperOut()
>>> manager
<test.UpperOut object at 0x10e673358>
>>> monster = manager.__enter__()
>>> monster
'THIS IS RETURN'
>>> monster == 'this is return'
TRUE
>>> manager
<TEST.UPPEROUT OBJECT AT 0X10E673358>
>>> manager.__exit__(None, None, None)
>>> monster
'this is return'
以上代码,我写了一个上下文管理器,用来把所有的输出都显示为大写。通过 UpperOut() 得到一个上下文管理器对象,而调用__enter__后进入上下文管理,在这之间所有的输出都是大写,在调用__exit__方法后结束上下文管理,可以看到monster的输出变为小写。
注意:
在了解上下文管理器后,学习with就很简单了,上下文管理器对象就是用来控制with语句的。
>>> from test import UpperOut
>>> with UpperOut() as word:
... print("this is with test")
... print(word)
...
THIS IS WITH TEST
THIS IS RETURN
>>> word
'this is return'
可以看出,在with代码块中,所有的输出都是大写,那么UperOut()对象中的__enter__()方法是什么时候调用的呢?
@contextmanager是contextlib模块中的工具,用于把生成器函数变成上下文管理器,就可以不需要创建类去实现管理器协议(即需要实现__enter__和__exit__)。
import contextlib
import sys
@contextlib.contextmanager
def upper_out():
original_write = sys.stdout.write
def upper_write(word):
original_write(word.upper)
sys.stdout.write = upper_write
yield "this is return"
sys.stdout.write = original_write
>>> from test import upper_out
>>> with upper_out() as word:
... print("this is with test")
... print(word)
...
THIS IS WITH TEST
THIS IS RETURN
>>> word
'this is return'
在@contextlib.contextmanager的装饰下,yield并不是用来生成生成器,而是用来把函数中的代码分成两部分,此装饰器会把函数包装成实现__enter__和__exit__方法的类。
但是上述方法还有个问题就是如果在with中抛出异常,然后在yield表达式中再次抛出,但是在那里并没有处理错误的代码,upper_out函数会中止,sys.stdout.write则永远无法恢复,系统将会处于无效状态,所以要把yield表达式放到try/finally中。
import contextlib
import sys
@contextlib.contextmanager
def upper_out():
original_write = sys.stdout.write
def upper_write(word):
original_write(word.upper)
sys.stdout.write = upper_write
msg = ""
try:
yield "this is return"
except ZeroDivisionError:
msg = "Please do not divide by zero"
finally:
sys.stdout.write = original_write
if msg:
print(msg)
注意:
▼
往期精彩回顾
▼