Python 的with關鍵字


Python 的with關鍵字

看別人的代碼時,with關鍵字經常會出現,博主決定梳理一下with以及python中上下文(context)的概念

1. 上下文管理器概念

Context Manager指的是python在執行一段代碼前后,做的一些預處理和后處理,使得代碼塊運行處於一個小的環境(surrounding),出了這個小環境之后,資源釋放,環境中的各種配置也失效。

例如在打開文件需要關閉,連接數據庫后需要關閉連接。很多優雅第三方庫也會利用上下文使得對象進入特定的某種狀態。

2. with關鍵字

with的基本用法如下:

with EXPR as VAR:
  BLOCK

其中發生了一系列過程:

  1. EXPR語句被執行,得到ContextManager
  2. 調用ContextManager.__enter__方法
  3. 如果有as VAR,則ContextManager.__enter__的返回值賦給VAR,否則就不管返回值
  4. 執行BLOCK,如果有VAR被用到,就和普通變量一樣
  5. 調用ContextManager.__exit__方法
    • __exit__有三個參數:type, value, traceback,BLOCK出異常時會得到對應值,正常情況就都為None
    • __exit__返回值為True表示BLOCK中出現的異常可以忽略,False表示需要raise

3. 例子

3.1 資源操作:

class CustomOpen:
    def __init__(self, filename: str):
        self.__filename = filename
        self.__handler = None
    
    def __enter__(self):
        print("enter......")
        self.__handler = open(self.__filename)
        return self.__handler
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit...", exc_type, exc_val, exc_tb)
        if self.__handler is not None:
            self.__handler.close()
        return True


with CustomOpen("hello.txt") as f:
    print(f.read())

運行結果:

enter......
hello world
exit... None None None

3.2 狀態維護

class CustomBrain:
    def __init__(self):
        self.__status = "normal"
    
    def say(self):
        if self.__status == "normal":
            print("You're a great man")
        elif self.__status == "special":
            print("You are a very outstanding person ")

    def __enter__(self):
        self.__status = "special"

    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.__status = "normal"


brain = CustomBrain()
brain.say() # 普通狀態

# 可以通過上下文維護一些狀態
with brain:
    brain.say() # 特殊狀態

brain.say() # 普通狀態

運行結果:

You're a great man
You are a very outstanding person
You're a great man

4. 使用contextlib簡化編寫

python內置的標准庫contextlib可以是的代碼書寫更加簡潔,本質是一樣的。比較有用的是contextlib.contextmanager這個裝飾器,被裝飾的函數在yield的前面相當於__enter__,yield的后面相當於__exit__,yield本身的返回值賦給as后的變量

所以第一個示例可以這么寫:

from contextlib import contextmanager

@contextmanager
def custom_open(filename: str):
    print("enter......")
    handler = open(filename)

    yield handler

    print("exit...")
    handler.close()


with custom_open("hello.txt") as f:
    print(f.read())

還是優雅了許多~


免責聲明!

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



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