前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python else与上下文管理器

Python else与上下文管理器

作者头像
用户7685359
发布2020-08-24 16:18:41
4220
发布2020-08-24 16:18:41
举报
文章被收录于专栏:FluentStudyFluentStudy

else的用法

大家比较熟悉的else是与if搭配,如果if条件不成立,则执行else里的内容。但是并不是只有if才可以和else搭配,for,while和try都可以搭配else。

  • 在for和while搭配else时,循环完后执行else中的内容,如果循环被break打断,则不执行else中的内容。
  • 在try搭配else时,else 放到except后,如果try中没有发生异常,则执行else中的内容,如果发生异常,则执行except中的内容。

上下文管理器

上下文管理器可以对上下文进行管理,上下文管理器中有两个必须的方法:__enter__和__exit__。当你使用上下文管理器对象调用__enter__方法时,就进入了上下文管理中,__enter__中的操作会对上下文进行影响,要停止上下文管理时,就调用__exit__方法停止上下文管理。

代码语言:javascript
复制
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 中调试结果如下:

代码语言:javascript
复制
>>> 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的输出变为小写。

注意:

  • __enter__和__exit__都要用上下文管理器对象进行调用
  • __enter__里除了self外不传入任何值,返回值可以是任意类型。
  • __exit__里面接收三个值,分别是exc_type(异常类), exc_value(异常值), traceback(traceback对象),如果__exit__返回True,表示所有的异常都已经处理完毕,如果返回None或True以外的值,with中的任何异常都会向上冒泡。

with

在了解上下文管理器后,学习with就很简单了,上下文管理器对象就是用来控制with语句的。

代码语言:javascript
复制
>>> 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__()方法是什么时候调用的呢?

  • with后面的表达式得到结果是上下文管理器对象
  • 在as的时候会把__enter__方法返回的结果绑定到word上
  • 在with代码块内进行上下文管理
  • 在退出with代码块时,上下文管理器对象会自动调用__exit__方法

@contextmanager

@contextmanager是contextlib模块中的工具,用于把生成器函数变成上下文管理器,就可以不需要创建类去实现管理器协议(即需要实现__enter__和__exit__)。

代码语言:javascript
复制
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
代码语言:javascript
复制
>>> 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__方法的类。

  • yield语句前的所有代码在with开始时执行(即调用__enter__方法时,yield的值为__enter__方法返回值,赋值给as后的变量)
  • yield后的所有代码在退出with代码块时执行(即调用__exit__方法时)

但是上述方法还有个问题就是如果在with中抛出异常,然后在yield表达式中再次抛出,但是在那里并没有处理错误的代码,upper_out函数会中止,sys.stdout.write则永远无法恢复,系统将会处于无效状态,所以要把yield表达式放到try/finally中。

代码语言:javascript
复制
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)

注意:

  • 使用contexmanager装饰器时,要把yield语句放在try/finally中,因为无法知道上下文管理中会发生什么错误。
  • 使用@contexmanager装饰器时,装饰器提供的__exit__方法会假定异常都得到了处理,如果不想让@contexmanage压制异常,必要要在被装饰的函数中显示重新抛出异常。

往期精彩回顾

由浅入深彻底了解 Python 闭包和装饰器

字符串拼接的N种方式

Iterable、Sequece、Iterator、generator

Python partial()最通俗的理解

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FluentStudy 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • else的用法
  • 上下文管理器
  • with
  • @contextmanager
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档