zoukankan      html  css  js  c++  java
  • python上下文管理器和with语句

    python中的with语句是一个晦涩的特性,但是有助于编写更加清晰易读的python代码。它有助于简化一些通用的资源管理模式,抽象出其中的功能,将其分解并重用。

    Python标准库中的内置的open()函数就是一个很好的用例:

    1 with open('hello.txt', 'w') as f:
    2     f.write('hello world')

    打开文件时一般建议使用with语句,因为这样能够确保打开的文件描述符在程序执行离开时with语句的上下文后自动关闭。本质上说,上面的代码可以替换成下面这样:

    1 f = open('hello.txt', 'w')
    2 try:
    3     f.write('hello world')
    4 finally:
    5     f.close()

    上面这段代码比with语句冗长。但是其中的try......finally语句很重要,只关注其中的逻辑代码还不够:

    1 f = open('hello.txt', 'w')
    2 f.write('hello world')
    3 f.close()

    如果在调用f.write()时发生异常,这段代码不能保证文件最后被关闭,因此程序可能会泄露文件描述符。因此with语句就可以派上用场,它能够简化资源的获取和释放。

    with语句不仅让处理系统资源的代码更易读,而且由于绝对不会忘记清理或者释放资源,因此还可以避免bug或者资源泄露。

    在自定义对象中支持with

    只要实现上下文管理器(context manager),就可以在自定义的类和函数中获得同样的功能。

    何为上下文管理器,这是一个简单的"协议"(或者接口),自定义对象需要遵循这个接口来支持with语句。总的来说,如果想将一个对象作为上下文管理器,需要做的就是向其中添加__enter__和__exit__方法。python将在资源管理周期的适当时间调用这两种方法。

    下面是一个上下文管理器的简单实现:

    1 class ManagedFile:
     2     def __int__(self, name):
     3         self.name = name
     4         
     5     def __enter__(self):
     6         self.file = open(self.name, 'w')
     7         return self.file
     8     
     9     def __exit__(self, exc_type, exc_val, exc_tb):
    10         if self.file:
    11             self.file.close()

    其中的ManagedFile类遵循上下文管理器协议,所以与原来的open()例子一样,也支持with语句:

    1 with ManagedFile('hello.txt') as f:
    2     f.write('hello world')
    3     f.write('bye now')

    运行结果:

     当执行流程进入with语句上下文时,python会调用__enter__获取资源,离开with上下文时,python会调用__exit__释放资源。

    在python中,除了编写基于类的上下文管理器来支持with语句外,标准库中的contextlib模块在上下文管理器基本协议的基础上提供了更多抽象。

    使用contextlib.contextmanager装饰器能够为资源定义一个基于生成器的工厂函数,该函数将自动支持with语句。下面的示例使用这种技术重写了之前的ManagedFile上下文管理器:

     1 from contextlib import contextmanager
     2 
     3 
     4 @contextmanager
     5 def managed_file(name):
     6     try:
     7         f = open(name, 'w')
     8         yield f
     9     finally:
    10         f.close()
    11 
    12 
    13 with managed_file('hello.txt') as f:
    14     f.write('hello world!')
    15     f.write('
    bye now')

    运行结果如下:

     这个managed_file()是生成器,开始先获取资源,之后暂停执行并产生资源以供调用者使用。当调用者离开with上下文时,生成器继续执行升序的清理步骤,并将资源释回系统。

    基于类的实现和基于生成器的实现基本上是等价的。

    用上下文管理器编写漂亮的API

    上下文管理器非常灵活,巧妙地使用with语句能够为模块和类定义方便的API。

     1 class Indenter:
     2     def __init__(self):
     3         self.level = 0
     4 
     5     def __enter__(self):
     6         self.level += 1
     7         return self
     8 
     9     def __exit__(self, exc_type, exc_val, exc_tb):
    10         self.level -= 1
    11 
    12     def print(self, text):
    13         print('   ' * self.level + text)
    14 
    15 
    16 with Indenter() as indent:
    17     indent.print('hi!')
    18     with indent:
    19         indent.print('hello!')
    20         with indent:
    21             indent.print('bonjour')
    22     indent.print('hey')

    运行结果如下:

     总结

    • with语句通过在所谓的上下文管理器中封装try.....finally语句的标准用法来简化异常处理。
    • with语句一般用来管理系统资源的安全获取和释放。资源首先由with语句获取,并在执行离开with上下文时自动释放。
    • 有效地使用with有助于避免资源泄露地问题,让代码更加易于阅读。
    
    
  • 相关阅读:
    布局神器display:table-cell
    解决IE兼容总汇【转】
    jQuery Validate【强大的表单验证】
    使用信号SIGALRM 为 recvfrom 设置超时,防止死等待
    并发服务器(IO多路复用)
    并发服务器(多进程版本)
    TCP listen()函数内幕
    c++ 重载运算符规则
    内核定时器struct timer_list
    C和arm汇编的相互调用(看书笔记)
  • 原文地址:https://www.cnblogs.com/xiebinbbb/p/13935975.html
Copyright © 2011-2022 走看看