python 上下文管理器使用方法小结


Posted in Python onOctober 10, 2017

上下文管理器最常用的是确保正确关闭文件,

with open('/path/to/file', 'r') as f:
 f.read()

with 语句的基本语法,

with expression [as variable]:

  with-block

expression是一个上下文管理器,其实现了enter和exit两个函数。当我们调用一个with语句时, 依次执行一下步骤,

1.首先生成一个上下文管理器expression, 比如open('xx.txt').
2.执行expression.enter()。如果指定了[as variable]说明符,将enter()的返回值赋给variable.
3.执行with-block语句块.
4.执行expression.exit(),在exit()函数中可以进行资源清理工作.

with语句不仅可以管理文件,还可以管理锁、连接等等,如,

#管理锁
import threading
lock = threading.lock()
with lock:
 #执行一些操作
 pass

#数据库连接管理
def test_write():
 sql = """  #具体的sql语句
 """
 con = DBConnection()
 with con as cursor: 
  cursor.execute(sql)
  cursor.execute(sql)
  cursor.execute(sql)

自定义上下文管理器

上下文管理器就是实现了上下文协议的类 ,也就是要实现 __enter__()和__exit__()两个方法。

__enter__():主要执行一些环境准备工作,同时返回一资源对象。如果上下文管理器open("test.txt")的enter()函数返回一个文件对象。
__exit__():完整形式为exit(type, value, traceback),这三个参数和调用sys.exec_info()函数返回值是一样的,分别为异常类型、异常信息和堆栈。如果执行体语句没有引发异常,则这三个参数均被设为None。否则,它们将包含上下文的异常信息。_exit()方法返回True或False,分别指示被引发的异常有没有被处理,如果返回False,引发的异常将会被传递出上下文。如果exit()函数内部引发了异常,则会覆盖掉执行体的中引发的异常。处理异常时,不需要重新抛出异常,只需要返回False,with语句会检测exit()返回False来处理异常。

class test_query:
  def __init__(self):
    pass

  def query(self):
    print('query')

  def __enter__(self):
    # 如果是数据库连接就可以返回cursor对象
    # cursor = self.cursor
    # return cursor
    print('begin query,')
    return self #由于这里没有资源对象就返回对象
    
  def __exit__(self,exc_type,exc_value,traceback):
    if traceback is None:
      print('End')
    else:
      print('Error')

    # 如果是数据库连接提交操作
    # if traceback is None:
    #  self.commit()
    # else:
    #  self.rollback()
    
if __name__ == '__main__':
  with test_query() as q:
    q.query()

contextlib

编写enter和exit仍然很繁琐,因此Python的标准库contextlib提供了更简单的写法,

基本语法

@contextmanager
    def some_generator(<arguments>):
      <setup>
      try:
        yield <value>
      finally:
        <cleanup>

生成器函数some_generator就和我们普通的函数一样,它的原理如下:
some_generator函数在在yield之前的代码等同于上下文管理器中的enter函数.
yield的返回值等同于enter函数的返回值,即如果with语句声明了as <variable>,则yield的值会赋给variable.
然后执行<cleanup>代码块,等同于上下文管理器的exit函数。此时发生的任何异常都会再次通过yield函数返回。
例,
自动加括号

import contextlib

@contextlib.contextmanager
def tag(name):
  print('<{}>'.format(name))
  yield
  print('</{}>'.format(name))


if __name__ == '__main__':
  with tag('h1') as t:
    print('hello')
    print('context')

管理锁

@contextmanager
def locked(lock):
  lock.acquire()
  try:
    yield
  finally:
    lock.release()

with locked(myLock):
  #代码执行到这里时,myLock已经自动上锁
  pass
  #执行完后会,会自动释放锁

管理文件关闭

@contextmanager
def myopen(filename, mode="r"):
  f = open(filename,mode)
  try:
    yield f
  finally:
    f.close()

with myopen("test.txt") as f:
  for line in f:
    print(line)

管理数据库回滚

@contextmanager
def transaction(db):
  db.begin()
  try:
    yield 
  except:
    db.rollback()
    raise
  else:
    db.commit()

with transaction(mydb):
  mydb.cursor.execute(sql)
  mydb.cursor.execute(sql)
  mydb.cursor.execute(sql)
  mydb.cursor.execute(sql)

这就很方便!
如果一个对象没有实现上下文,我们就不能把它用于with语句。这个时候,可以用closing()来把该对象变为上下文对象。它的exit函数仅仅调用传入参数的close函数.
例如,用with语句使用urlopen():

import contextlib
from urllib.request import urlopen

if __name__ == '__main__':
  with contextlib.closing(urlopen('https://www.python.org')) as page:
    for line in page:
      print(line)
closing也是一个经过@contextmanager装饰的generator,这个generator编写起来其实非常简单:

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

嵌套使用

import contextlib
from urllib.request import urlopen

if __name__ == '__main__':
  with contextlib.closing(urlopen('https://www.python.org')) as page,\
      contextlib.closing(urlopen('https://www.python.org')) as p:
    for line in page:
      print(line)
        print(p)

在2.x中需要使用contextlib.nested()才能使用嵌套,3.x中可以直接使用。

更多函数可以参考官方文档https://docs.python.org/3/library/contextlib.html

Python 相关文章推荐
python3.3实现乘法表示例
Feb 07 Python
python僵尸进程产生的原因
Jul 21 Python
使用TensorFlow实现SVM
Sep 06 Python
对Python的zip函数妙用,旋转矩阵详解
Dec 13 Python
BP神经网络原理及Python实现代码
Dec 18 Python
更新修改后的Python模块方法
Mar 03 Python
python实现动态数组的示例代码
Jul 15 Python
使用 Python 快速实现 HTTP 和 FTP 服务器的方法
Jul 22 Python
Python基于Hypothesis测试库生成测试数据
Apr 29 Python
Python实现常见的几种加密算法(MD5,SHA-1,HMAC,DES/AES,RSA和ECC)
May 09 Python
利用python中的matplotlib打印混淆矩阵实例
Jun 16 Python
Python实战之实现康威生命游戏
Apr 26 Python
python中如何使用正则表达式的集合字符示例
Oct 09 #Python
python中如何使用正则表达式的非贪婪模式示例
Oct 09 #Python
Python 多进程和数据传递的理解
Oct 09 #Python
Python 加密的实例详解
Oct 09 #Python
Python 3实战爬虫之爬取京东图书的图片详解
Oct 09 #Python
Python3实战之爬虫抓取网易云音乐的热门评论
Oct 09 #Python
Python读取文件内容的三种常用方式及效率比较
Oct 07 #Python
You might like
openPNE常用方法分享
2011/11/29 PHP
PHP中如何实现常用邮箱的基本判断
2014/01/07 PHP
QQ互联一键登录审核不通过的解决方案
2014/09/10 PHP
smarty简单应用实例
2015/11/03 PHP
javascript下过滤数组重复值的代码
2007/09/10 Javascript
你必须知道的Javascript知识点之&quot;单线程事件驱动&quot;的使用
2013/04/23 Javascript
密码框显示提示文字jquery示例
2013/08/29 Javascript
JavaScript判断用户是否对表单进行了修改的方法
2015/03/18 Javascript
JS实现可点击展开与关闭的左侧广告代码
2015/09/02 Javascript
深入探究AngularJS框架中Scope对象的超级教程
2016/01/04 Javascript
angular实现form验证实例代码
2017/01/17 Javascript
遍历json获得数据的几种方法小结
2017/01/21 Javascript
vue 路由嵌套高亮问题的解决方法
2018/05/17 Javascript
JavaScript中发出HTTP请求最常用的方法
2018/07/12 Javascript
使用vue-router切换页面时,获取上一页url以及当前页面url的方法
2019/05/06 Javascript
node.js命令行教程图文详解
2019/05/27 Javascript
layui form表单提交之后重新加载数据表格的方法
2019/09/11 Javascript
js实现点击上传图片并设为模糊背景
2020/08/02 Javascript
[02:49:21]2019完美盛典全程录像
2019/12/08 DOTA
django 删除数据库表后重新同步的方法
2018/05/27 Python
Python脚本完成post接口测试的实例
2018/12/17 Python
linux环境下Django的安装配置详解
2019/07/22 Python
Django models.py应用实现过程详解
2019/07/29 Python
Python +Selenium解决图片验证码登录或注册问题(推荐)
2020/02/09 Python
把Anaconda中的环境导入到Pycharm里面的方法步骤
2020/10/30 Python
详解Sticky Footer 绝对底部的两种套路
2017/11/03 HTML / CSS
Draper James官网:知名演员瑞茜·威瑟斯彭所创品牌
2017/10/25 全球购物
Myprotein瑞士官方网站:运动营养和健身网上商店
2019/09/25 全球购物
实习期自我鉴定
2013/10/11 职场文书
高二英语教学反思
2014/01/19 职场文书
2014年党务工作总结
2014/11/25 职场文书
幼儿园班级管理心得体会
2016/01/07 职场文书
《悬崖边的树》读后感2篇
2019/12/02 职场文书
pytorch中的torch.nn.Conv2d()函数图文详解
2022/02/28 Python
CI Games宣布《堕落之王2》使用虚幻引擎5制作 预计将于2023年正式发售
2022/04/11 其他游戏
Win10多屏显示如何设置?Win10电脑多屏显示设置操作方法
2022/07/07 数码科技