python上下文管理

一、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()
View Code

  上述代码很简单,针对数据类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装饰器,实现了数据库连接和释放的代码的复用,对比上面的代码,可以发现,使用装饰器相对优雅了很多

扫描二维码关注公众号,回复: 5160228 查看本文章

  但是每个装饰器都需要先定义一下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:\Users\admin\Desktop\1.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:\Users\admin\Desktop\1.txt"),获得上下文管理器

  2)加载上下文管理器的__enter__ 和 __exit__,以备后续调用

  3)调用__enter__(),方法,并返回文件对象 f 赋值给as从句变量

  4)执行with语句块的子语句块f.write("something")

  5)调用上下文管理器的__exit__(),如果是由于异常导致程序退出的,将type, value 和traceback作为参数传递给exit(),否则传三个None

     如果__exit__()返回值等于False,则异常会被显示的抛出,程序终止,否则,异常会被无视,继续执行。

   上下文管理器主要用于多线程/进程的变成中,因为涉及到上下文的切换

  

  

猜你喜欢

转载自www.cnblogs.com/fiona-zhong/p/10335009.html