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裝飾器,實現了數據庫連接和釋放的代碼的復用,對比上面的代碼,可以發現,使用裝飾器相對優雅了很多

  但是每個裝飾器都需要先定義一下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,則異常會被顯示的拋出,程序終止,否則,異常會被無視,繼續執行。

   上下文管理器主要用於多線程/進程的變成中,因為涉及到上下文的切換

  

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM