一、python上下文介绍:
python中的上下文,指的就是代码所执行的环境状态,或者运行的场景
python上下文管理器规定了某个对象的使用范围,当进入或者离开了使用范围,会有相应的操作,多用于资源的分配和释放上,即在开始时分配资源,结束时释放资源。
如文件的读写,在读写前,需要先打开文件,在读写完成后,需要关闭文件。再如数据库的操作,在操作前,需要先连接数据库,结束后,需要释放连接等。
二、上下文管理器的使用:
下面看看资源的创建和释放场景(以数据库的操作为例):
class DataBase(object): def __init__(self): self.connect = False def connect(self): self.connect = True def close(self): self.connect = False def query(self): if self.connect: return "query data" else: raise ValueError("db not connected") def handle_query(): db = DataBase() db.connect() print("db query:", db.query()) db.close() if __name__=="__main__": handle_query()
上述代码很简单,针对数据类DataBase,我们提供了connect,close,query三个常规的db交互接口。
普通的功能实现是没有问题的,但是我们都知道,在web交互中,数据库的操作是最频繁的,也就是说,我们每次在操作数据库的时候,都需要进行数据库类实例化,连接,关闭操作。显然这个和pythonic不符合的。
然后为了上面的代码更加的优雅,我们想到了python中的一个黑魔法,装饰器。
我们可以将上述代码数据库的连接和关闭封装成一个闭包,然后通过装饰器对上面的代码进行改写:
class DataBase(object): def __init__(self): self.connect = False def conn(self): self.connect = True def close(self): self.connect = False def query(self): if self.connect: return "query data" else: raise ValueError("db not connected") def dbconn(fun): def wrapper(*args, **kwargs): db = DataBase() db.conn() fun(db, *args, **kwargs) db.close() return wrapper @dbconn def handle_query(db=None): print("query:", db.query) if __name__=="__main__": handle_query()
我们封装了一个dbconn的装饰器,通过使用@dbconn装饰器,实现了数据库连接和释放的代码的复用,对比上面的代码,可以发现,使用装饰器相对优雅了很多
但是每个装饰器都需要先定义一下db的资源句柄,看起来还是没有那么优雅。下面我们通过上下文管理器的方式实现数据库操作:
class DataBase(object): def __init__(self): self.connect = False def conn(self): self.connect = True def close(self): self.connect = False def query(self): return "query data" def __enter__(self): self.conn() return self def __exit__(self, exc_type, exc_value, exc_db): self.close() def handle_query(): with DataBase() as db: print("query:", db.query) if __name__=="__main__": handle_query()
对比上面的上下文管理器实现数据库操作的代码,可以发现,在定义数据库类DataBase的时候,虽然多写了几行代码,但是我们的代码可读性变得更好了。
三、上下文管理协议
实现了上下文协议的对象叫上下文管理器
那么什么是上下文协议呢?即实现了__enter__() 和__exit__()方法,也就是实现了上下文管理协议
上下文表达式必须要返回一个上下文管理器对象
代码结构如下:
class Context: def __init__(self, filename, fileMode="w"): self.obj = open(filename, fileMode) def __enter__(self): return self.obj def __exit__(self, exc_type, exc_value, exc_db): self.obj.close() with Context("C:UsersadminDesktop1.txt") as f: f.write("something")
如上图,Context类实现了__enter__和__exit__两个上下文管理器协议,当Context被调用或实例化的时候,创建了上下文管理器
配合with语句使用的时候,上下文管理器会自动调用__enter__方法,然后进入运行时上下文环境,并将__enter__函数返回值赋值给as从句变量 f
当f.write("something")执行完毕退出with语句块或者是出现异常导致程序退出时,会执行__exit__方法,并把异常信息传递给后面的参数exc_type, exc_value, exc_db
如果__exit__方法返回True,则with语句块不会显示的抛出异常,程序终止
如果__exit__方法返回None或者False,异常会被主动抛出,程序终止。
四、上下文管理器工具
python为了使代码更加的优雅,还提供了一个模块contextlib 来实现更函数式的上下文管理器
from contextlib import contextmanager @contextmanager def context(filename, fileMode="w"): f = open(filename, fileMode) try: yield f except Exception as e: print(e) finally: f.close() with context("C:\Users\admin\Desktop\1.txt") as f: f.write("write something1")
上图通过contextmanager装饰器定义了一个上下文管理器函数context,通过with context("C:\Users\admin\Desktop\1.txt") 返回上下文管理器对象
然后调用函数隐式的__enter__函数,并将结果通过yield返回
然后通过返回的文件对象,执行文件的写入操作
写入操作结束,退出上下文环境,执行了f.close()
五、with语句执行过程总结:
1)执行上下文表达式 Context("C:UsersadminDesktop1.txt"),获得上下文管理器
2)加载上下文管理器的__enter__ 和 __exit__,以备后续调用
3)调用__enter__(),方法,并返回文件对象 f 赋值给as从句变量
4)执行with语句块的子语句块f.write("something")
5)调用上下文管理器的__exit__(),如果是由于异常导致程序退出的,将type, value 和traceback作为参数传递给exit(),否则传三个None
如果__exit__()返回值等于False,则异常会被显示的抛出,程序终止,否则,异常会被无视,继续执行。
上下文管理器主要用于多线程/进程的变成中,因为涉及到上下文的切换