Python上下文管理器contextlib.ContextManager,__enter__,__exit__

背景:常用的with语句允许开发者创建上下文管理器。什么是上下文管理器?上下文管理器就是允许你可以自动地开始和结束一些事情。例如,你可能想要打开一个文件,然后写入一些内容,最后再关闭文件。这或许就是上下文管理器中一个最经典的示例。事实上,当你利用with语句打开一个文件时,Python替你自动创建了一个上下文管理器。

1、with语句

最常用的方式 打开一个文件,with语句在你写入文件后替你关闭了文件。
不使用with语句

f = open("1.txt","w")
f.write("hello world")
f.close()

使用with语句

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

# 其实上面的代码等价于 try--finally语句
try:
    f = open('1.txt', 'w')
    f.write('hello world')
finally:
	if f:
        f.close()

上下文管理器背后工作的机制是使用Python的方法:enter__和__exit

2、使用__enter__()和__exit__()实现自定义类实现open方法

上例中open()方法是Python自带的,那我们怎么定义自己的类型来使用with语句呢。其实只要你的类定义了__enter__()和__exit__()方法,就可以使用Python的上下文管理器了。enter()方法会在with语句进入时被调用,其返回值会赋给as关键字后的变量;而__exit__()方法会在with语句块退出后自动被调用。

# 上面的with语句 等价于下面的代码
class OpenFile(object):
    def __init__(self, filename):
        self.file_name = filename

    def __enter__(self):
        self.f = open(self.file_name, 'a+')
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.f.close()

with OpenFile('1.txt') as f:
    f.write('hello world')
3、使用contextlib.ContextManager 替代__enter__()和__exit__()实现open方法

这个装饰器常与yield一起用
@contextlib.contextmanager是一个装饰器,由它修饰的方法会有两部分构成,中间由yield关键字分开。由此方法创建的上下文管理器,在代码块执行前会先执行yield上面的语句;在代码块执行后会再执行yield下面的语句。

import contextlib

@contextlib.contextmanager
def file_open(path):
    try:
        f = open(path,"w")
        yield f
    except Exception as e:
        print(e.message)
    finally:
        print("Closing file")
        if f:
            f.close()

if __name__ == "__main__":
    with file_open("1.txt") as f:
        f.write("hello world")

4、耗时统计demo
import contextlib
import time

@contextlib.contextmanager
def cost_time(title):
    print('cost time start...')
    start = time.time()
    yield
    print('cost time end...')
    end = time.time()
    usedTime = (end - start) * 1000
    print('cost time %d ms' % usedTime)
    print('--'*20)


with cost_time(1):
    print('3...')
    time.sleep(1)

with cost_time(2):
    print('4...')
    time.sleep(2)

输出结果如下:

cost time start...
3...
cost time end...
cost time 1000 ms
----------------------------------------
cost time start...
4...
cost time end...
cost time 2000 ms
----------------------------------------
5、实际项目案例 创建数据库链接

import contextlib
import MySQLdb

@contextlib.contextmanager
def connect_db(dict_cursor=True):
    conn, cursor = None, None
    try:
        conf = {
        	"db_host": "xxx",
        	"db_port": "xxx",
        	"db_user": "xxx",
        	"db_pswd": "xxx",
        	"db_name": "xxx",
        }
        conn = MySQLdb.connect(
                host=conf["db_host"], port=int(conf["db_port"]), user=conf["db_user"],
                passwd=conf["db_pswd"], db=conf["db_name"], charset="utf8", autocommit=False)
        cursor = conn.cursor(MySQLdb.cursors.DictCursor) if dict_cursor else conn.cursor()
        yield cursor
        conn.commit()
    except Exception as e:
        if cursor:
            cursor.close()
        raise e
    finally:
        if cursor:
            cursor.close()
        if conn:
            conn.close()

参考链接:
https://www.cnblogs.com/zknublx/p/11726572.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值