美文网首页
with and contextmanager

with and contextmanager

作者: 小混混_f0f2 | 来源:发表于2018-06-23 00:35 被阅读0次

    with

    初识with

    在Python中,读写文件这样的资源要特别注意,必须在使用完毕后正确关闭它们。正确关闭文件资源的一个方法是使用try...finally

    try:
        f = open("/directory/filename", 'r')
        f.read()
    finally:
          # 如果f不存在, 则应该是文件对象未打开
        if f:
            f.close()
    

    但是写try:...finally:...非常繁琐。Python的with语句允许我们非常方便地使用资源,而不必担心资源没有关闭,所以上面的代码可以简化为:

    # 注意f只会在with语句块有效
    with open("/directory/filename", 'r') as f:
            f.read()
    

    with的工作原理

    并不是只有open()函数返回的fp对象才能使用with语句。实际上,任何对象,只要正确实现了上下文管理,就可以用于with语句。

    实现上下文管理是通过__enter____exit__这两个方法实现的. 我们可以简单通过一个例子来说明with的内部调用方法

    class Generator(object):
    
        def __init__(self, name):
            self.name = name
    
        def __enter__(self):
            print("There's no way out")
            return self
    
        def __exit__(self, exc_type, exc_value, traceback):
              # 检查退出的类型
            # 当有异常发生时 exc_type存在值
            if exc_type:
                print('Open the door')
            else:
                print('100%')
    
        def generate(self):
            print('%s can uses love to generate electricity' % self.name)
    

    我们可以通过以下代码调用

    # 紧跟with后面的语句被求值后,返回对象的 `__enter__() `方法被调用,
    # 这个方法的返回值将被赋值给as后面的变量    
    with Generator("LEX") as lex:
          lex.generate()
    # 当with后面的代码块全部被执行完之后,将调用返回对象的 `__exit__()`方法。
    
    用爱发电

    其实 with 就是这么简单, 大部分初学者只要理解到这里就可以了, 所以小白勿入, 高能级别

    高能预警

    上下文管理器

    我知道有很多小白还是读了, 那我先介绍几个名词让你们知男而退

    • 上下文管理协议(Context Management Protocol):包含方法 __enter____exit__,支持

    该协议的对象要实现这两个方法。

    • 上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了

    __enter____exit__方法。上下文管理器定义执行 with 语句时要建立的运行时上下文,

    负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,

    也可以通过直接调用其方法来使用。

    • 运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的 __enter__

    __exit__ 方法实现,__enter__ 方法在语句体执行之前进入运行时上下文,__exit__

    语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。

    • 上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式

    要返回一个上下文管理器对象。如 例子with后面的Generator("LEX") as lex:

    • 语句体(with-body):with 语句包裹起来的代码块,在执行语句体之前会调用上下文管

    理器的__enter__方法,执行完语句体之后会执行 __exit__ 方法。

    其实这些术语不需要你们立刻理解, 只需要在以后的课程或者项目中慢慢体会.

    @contextmanager

    编写__enter____exit__仍然很繁琐,因此Python的标准库contextlib提供了更简单的写法, 让我们更加简洁的定制自己的上下文管理器. 同样的爱之代码走起:

    from context import contextmanager
    
    class Generator(object):
    
        def __init__(self, name):
            self.name = name
    
        def generate(self):
            print('%s can uses love to generate electricity' % self.name)
    
    @contextamnager
    def create_generator(name):
          print("I'm generating electricity from love again")
          lex = Generator(name)
        # 返回给with...as...语句
        yield lex
        print('100%')
    

    @contextmanager这个decorator必须接受一个 ==generator== ,用yield语句把with ... as var把变量输出出去, 然后属于你的上下文管理器就弄了, 是不是出奇的简单

    好久没有这么简单

    使用一下这个管理器, 说说感觉

    with create_generator("LEX") as lex:
          lex.generate()
    
    感觉良好

    @closing

    但是少年郎们还没结束呢, 让我们了解一下closing 的作用, 它的作用就是让不是上下文管理器的对象转化为上下文管理器, 即可使用with语句

    urllib.request import urlopen
    
    with closing(urlopen('https://www.bilibili.com')) as page:
        for line in page:
            print(line)
    

    其实closing内部非常好实现, 内部还是调用了contextmanager方法

    @contextmanager
    def closing(thing):
        try:
            yield thing
        finally:
            thing.close()
    

    closing 不同于 @contextmanager, 他的本身不需要生成器, 可谓加上这行代码, 秒变真男人

    总结

    1. with 关联着对象的__exit____enter__方法, 你必须清楚他们是何时被调用, 他们的返回值又返回到哪里去了

    2. @contextmanager 所装饰的对象必须是带yield的生成器, yield 会把后面的值返回给with...as...语句

    3. @closing 原先必许在所装饰的对象定义一个close方法, 否则会报错, 但是现在不会, 所以这种一行真男人得灵药不多了

    4. 上下文管理器将会贯穿大型框架结构, 如flask的 Application上下文, 请求(request)上下文等等, 学习上下文管理器可以帮助我们更好理解那些框架的工作原理.

    未完待续...

    引用

    廖雪峰 contextlib

    相关文章

      网友评论

          本文标题:with and contextmanager

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