偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Python新增功能 詳解上下文管理器

開發(fā) 后端 前端
上下文管理器是在Python2.5加入的功能,它能夠讓你的代碼可讀性更強(qiáng)并且錯(cuò)誤更少。接下來,讓我們來看看該如何使用。

這篇文章是關(guān)于什么的?

  • 什么是Python中的上下文管理器
  • 怎么使用上下文管理器
  • 如何創(chuàng)建自己的上下文管理器
  • 關(guān)于Python上下文庫(kù)(contextlib)

1. 上下文管理器是什么?

舉個(gè)例子,你在寫Python代碼的時(shí)候經(jīng)常將一系列操作放在一個(gè)語句塊中:

當(dāng)某條件為真 – 執(zhí)行這個(gè)語句塊

當(dāng)某條件為真 – 循環(huán)執(zhí)行這個(gè)語句塊

有時(shí)候我們需要在當(dāng)程序在語句塊中運(yùn)行時(shí)保持某種狀態(tài),并且在離開語句塊后結(jié)束這種狀態(tài)。

所以,事實(shí)上上下文管理器的任務(wù)是 – 代碼塊執(zhí)行前準(zhǔn)備,代碼塊執(zhí)行后收拾。

上下文管理器是在Python2.5加入的功能,它能夠讓你的代碼可讀性更強(qiáng)并且錯(cuò)誤更少。接下來,讓我們來看看該如何使用。

context1

2. 如何使用上下文管理器?

看代碼是***的學(xué)習(xí)方式,來看看我們通常是如何打開一個(gè)文件并寫入”Hello World”?

  1. filename = 'my_file.txt' 
  2. mode = 'w' # Mode that allows to write to the file  
  3. writer = open(filename, mode)  
  4. writer.write('Hello ')  
  5. writer.write('World')  
  6. writer.close() 

1-2行,我們指明文件名以及打開方式(寫入)。

第3行,打開文件,4-5行寫入“Hello world”,第6行關(guān)閉文件。

這樣不就行了,為什么還需要上下文管理器?但是我們忽略了一個(gè)很小但是很重要的細(xì)節(jié):如果我們沒有機(jī)會(huì)到達(dá)第6行關(guān)閉文件,那會(huì)怎樣?

舉個(gè)例子,磁盤已滿,因此我們?cè)诘?行嘗試寫入文件時(shí)就會(huì)拋出異常,而第6行則根本沒有機(jī)會(huì)執(zhí)行。

當(dāng)然,我們可以使用try-finally語句塊來進(jìn)行包裝:

  1. writer = open(filename, mode)  
  2. try:  
  3.     writer.write('Hello ')  
  4.     writer.write('World')  
  5. finally:  
  6.     writer.close() 

finally語句塊中的代碼無論try語句塊中發(fā)生了什么都會(huì)執(zhí)行。因此可以保證文件一定會(huì)關(guān)閉。這么做有什么問題么?當(dāng)然沒有,但當(dāng)我們進(jìn)行一些比寫入“Hello world”更復(fù)雜的事情時(shí),try-finally語句就會(huì)變得丑陋無比。例如我們要打開兩個(gè)文件,一個(gè)讀一個(gè)寫,兩個(gè)文件之間進(jìn)行拷貝操作,那么通過with語句能夠保證兩者能夠同時(shí)被關(guān)閉。

OK,讓我們把事情分解一下:

首先,創(chuàng)建一個(gè)名為“writer”的文件變量。

然后,對(duì)writer執(zhí)行一些操作。

***,關(guān)閉writer。

這樣是不是優(yōu)雅多了?

  1. with open(filename, mode) as writer:  
  2.     writer.write('Hello ')   
  3.     writer.write('World'

讓我們深入一點(diǎn),“with”是一個(gè)新關(guān)鍵詞,并且總是伴隨著上下文管理器出現(xiàn)。“open(filename, mode)”曾經(jīng)在之前的代碼中出現(xiàn)。“as”是另一個(gè)關(guān)鍵詞,它指代了從“open”函數(shù)返回的內(nèi)容,并且把它賦值給了一個(gè)新的變量。“writer”是一個(gè)新的變量名。

2-3行,縮進(jìn)開啟一個(gè)新的代碼塊。在這個(gè)代碼塊中,我們能夠?qū)riter做任意操作。這樣我們就使用了“open”上下文管理器,它保證我們的代碼既優(yōu)雅又安全。它出色的完成了try-finally的任務(wù)。

open函數(shù)既能夠當(dāng)做一個(gè)簡(jiǎn)單的函數(shù)使用,又能夠作為上下文管理器。這是因?yàn)閛pen函數(shù)返回了一個(gè)文件類型(file type)變量,而這個(gè)文件類型實(shí)現(xiàn)了我們之前用到的write方法,但是想要作為上下文管理器還必須實(shí)現(xiàn)一些特殊的方法,我會(huì)在接下來的小節(jié)中介紹。

3. 自定義上下文管理器

讓我們來寫一個(gè)“open”上下文管理器。

要實(shí)現(xiàn)上下文管理器,必須實(shí)現(xiàn)兩個(gè)方法 – 一個(gè)負(fù)責(zé)進(jìn)入語句塊的準(zhǔn)備操作,另一個(gè)負(fù)責(zé)離開語句塊的善后操作。同時(shí),我們需要兩個(gè)參數(shù):文件名和打開方式。

Python類包含兩個(gè)特殊的方法,分別名為:__enter__以及__exit__(雙下劃線作為前綴及后綴)。

當(dāng)一個(gè)對(duì)象被用作上下文管理器時(shí):

__enter__ 方法將在進(jìn)入代碼塊前被調(diào)用。

__exit__ 方法則在離開代碼塊之后被調(diào)用(即使在代碼塊中遇到了異常)。

下面是上下文管理器的一個(gè)例子,它分別進(jìn)入和離開代碼塊時(shí)進(jìn)行打印。

  1. class PypixContextManagerDemo:  
  2.    
  3.     def __enter__(self):  
  4.         print 'Entering the block' 
  5.    
  6.     def __exit__(self, *unused):  
  7.         print 'Exiting the block' 
  8.    
  9. with PypixContextManagerDemo():  
  10.     print 'In the block' 
  11.    
  12. #Output:  
  13. #Entering the block  
  14. #In the block  
  15. #Exiting the block 

注意一些東西:

  • 沒有傳遞任何參數(shù)。
  • 在此沒有使用“as”關(guān)鍵詞。
  • 稍后我們將討論__exit__方法的參數(shù)設(shè)置。

我們?nèi)绾谓o一個(gè)類傳遞參數(shù)?其實(shí)在任何類中,都可以使用__init__方法,在此我們將重寫它以接收兩個(gè)必要參數(shù)(filename, mode)。

當(dāng)我們進(jìn)入語句塊時(shí),將會(huì)使用open函數(shù),正如***個(gè)例子中那樣。而當(dāng)我們離開語句塊時(shí),將關(guān)閉一切在__enter__函數(shù)中打開的東西。

以下是我們的代碼:

  1. class PypixOpen:  
  2.    
  3.     def __init__(self, filename, mode):  
  4.         self.filename = filename  
  5.         self.mode = mode  
  6.    
  7.     def __enter__(self):  
  8.         self.openedFile = open(self.filename, self.mode)  
  9.         return self.openedFile  
  10.    
  11.     def __exit__(self, *unused):  
  12.         self.openedFile.close()  
  13.    
  14. with PypixOpen(filename, mode) as writer:  
  15.     writer.write("Hello World from our new Context Manager!"

來看看有哪些變化:

3-5行,通過__init__接收了兩個(gè)參數(shù)。

7-9行,打開文件并返回。

12行,當(dāng)離開語句塊時(shí)關(guān)閉文件。

14-15行,模仿open使用我們自己的上下文管理器。

#p#

除此之外,還有一些需要強(qiáng)調(diào)的事情:

如何處理異常

我們完全忽視了語句塊內(nèi)部可能出現(xiàn)的問題。

如果語句塊內(nèi)部發(fā)生了異常,__exit__方法將被調(diào)用,而異常將會(huì)被重新拋出(re-raised)。當(dāng)處理文件寫入操作時(shí),大部分時(shí)間你肯定不希望隱藏這些異常,所以這是可以的。而對(duì)于不希望重新拋出的異常,我們可以讓__exit__方法簡(jiǎn)單的返回True來忽略語句塊中發(fā)生的所有異常(大部分情況下這都不是明智之舉)。

我們可以在異常發(fā)生時(shí)了解到更多詳細(xì)的信息,完備的__exit__函數(shù)簽名應(yīng)該是這樣的:

  1. def __exit__(self, exc_type, exc_val, exc_tb) 

這樣__exit__函數(shù)就能夠拿到關(guān)于異常的所有信息(異常類型,異常值以及異常追蹤信息),這些信息將幫助異常處理操作。在這里我將不會(huì)詳細(xì)討論異常處理該如何寫,以下是一個(gè)示例,只負(fù)責(zé)拋出SyntaxErrors異常。

  1. class RaiseOnlyIfSyntaxError:  
  2.    
  3.     def __enter__(self):  
  4.         pass 
  5.    
  6.     def __exit__(self, exc_type, exc_val, exc_tb):  
  7.         return SyntaxError != exc_type 

4. 談一些關(guān)于上下文庫(kù)(contextlib)的內(nèi)容

contextlib是一個(gè)Python模塊,作用是提供更易用的上下文管理器。

contextlib.closing

假設(shè)我們有一個(gè)創(chuàng)建數(shù)據(jù)庫(kù)函數(shù),它將返回一個(gè)數(shù)據(jù)庫(kù)對(duì)象,并且在使用完之后關(guān)閉相關(guān)資源(數(shù)據(jù)庫(kù)連接會(huì)話等)

我們可以像以往那樣處理或是通過上下文管理器:

  1. with contextlib.closing(CreateDatabase()) as database:  
  2.     database.query() 

contextlib.closing方法將在語句塊結(jié)束后調(diào)用數(shù)據(jù)庫(kù)的關(guān)閉方法。

contextlib.nested

另一個(gè)很cool的特性能夠有效地幫助我們減少嵌套:

假設(shè)我們有兩個(gè)文件,一個(gè)讀一個(gè)寫,需要進(jìn)行拷貝。

以下是不提倡的:

  1. with open('toReadFile''r') as reader:  
  2.     with open('toWriteFile''w') as writer:  
  3.         writer.writer(reader.read()) 

可以通過contextlib.nested進(jìn)行簡(jiǎn)化:

  1. with contextlib.nested(open('fileToRead.txt''r'),  
  2.                        open('fileToWrite.txt''w')) as (reader, writer):  
  3.     writer.write(reader.read()) 

在Python2.7中這種寫法被一種新語法取代:

  1. with open('fileToRead.txt''r') as reader, \  
  2.         open('fileToWrite.txt''w') as writer:  
  3.         writer.write(reader.read()) 

contextlib.contextmanager

對(duì)于Python高級(jí)玩家來說,任何能夠被yield關(guān)鍵詞分割成兩部分的函數(shù),都能夠通過裝飾器裝飾的上下文管理器來實(shí)現(xiàn)。任何在yield之前的內(nèi)容都可以看做在代碼塊執(zhí)行前的操作,而任何yield之后的操作都可以放在exit函數(shù)中。

這里我舉一個(gè)線程鎖的例子:

鎖機(jī)制保證兩段代碼在同時(shí)執(zhí)行時(shí)不會(huì)互相干擾。例如我們有兩塊并行執(zhí)行的代碼同時(shí)寫一個(gè)文件,那我們將得到一個(gè)混合兩份輸入的錯(cuò)誤文件。但如果我們能有一個(gè)鎖,任何想要寫文件的代碼都必須首先獲得這個(gè)鎖,那么事情就好辦了。如果你想了解更多關(guān)于并發(fā)編程的內(nèi)容,請(qǐng)參閱相關(guān)文獻(xiàn)。

下面是線程安全寫函數(shù)的例子:

  1. import threading  
  2.    
  3. lock = threading.Lock()  
  4.    
  5. def safeWriteToFile(openedFile, content):  
  6.     lock.acquire()  
  7.     openedFile.write(content)  
  8.     lock.release() 

接下來,讓我們用上下文管理器來實(shí)現(xiàn),回想之前關(guān)于yield和contextlib的分析:

  1. @contextlib.contextmanager  
  2. def loudLock():  
  3.     print 'Locking' 
  4.     lock.acquire()  
  5.     yield 
  6.     print 'Releasing' 
  7.     lock.release()  
  8.    
  9. with loudLock():  
  10.     print 'Lock is locked: %s' % lock.locked()  
  11.     print 'Doing something that needs locking' 
  12.    
  13. #Output:  
  14. #Locking  
  15. #Lock is locked: True  
  16. #Doing something that needs locking  
  17. #Releasing 

特別注意,這不是異常安全(exception safe)的寫法。如果你想保證異常安全,請(qǐng)對(duì)yield使用try語句。幸運(yùn)的是threading。lock已經(jīng)是一個(gè)上下文管理器了,所以我們只需要簡(jiǎn)單地:

  1. @contextlib.contextmanager  
  2. def loudLock():  
  3.     print 'Locking' 
  4.     with lock:  
  5.         yield 
  6.     print 'Releasing' 

因?yàn)閠hreading.lock在異常發(fā)生時(shí)會(huì)通過__exit__函數(shù)返回False,這將在yield被調(diào)用是被重新拋出。這種情況下鎖將被釋放,但對(duì)于“print ‘Releasing’”的調(diào)用則不會(huì)被執(zhí)行,除非我們重寫try-finally。

如果你希望在上下文管理器中使用“as”關(guān)鍵字,那么就用yield返回你需要的值,它將通過as關(guān)鍵字賦值給新的變量。

原文鏈接: pypix.com   翻譯: 伯樂在線 熊崽Kevin

譯文鏈接: http://blog.jobbole.com/64175/

責(zé)任編輯:林師授 來源: 伯樂在線
相關(guān)推薦

2025-06-06 08:00:00

上下文管理器Python開發(fā)

2022-06-29 14:15:01

Python計(jì)時(shí)器上下文管理器

2022-11-03 08:29:32

編程管理器協(xié)議

2023-11-16 08:46:27

上下文管理器Python

2024-11-14 09:00:00

Python上下文管理器

2023-12-10 13:37:23

Python編程上下文管理

2017-05-11 14:00:02

Flask請(qǐng)求上下文應(yīng)用上下文

2012-07-18 11:39:18

ibmdw

2012-12-31 10:01:34

SELinuxSELinux安全

2012-08-01 09:58:12

Mountain Li操作系統(tǒng)

2022-09-14 13:13:51

JavaScript上下文

2021-11-03 16:41:30

Windows 11操作系統(tǒng)微軟

2021-06-23 09:46:16

Python 3.10結(jié)構(gòu)模式管理器

2015-07-08 10:25:05

Javascript上下文作用域

2022-09-15 08:01:14

繼承基礎(chǔ)設(shè)施基礎(chǔ)服務(wù)

2022-10-28 16:24:33

Context上下文鴻蒙

2024-09-30 14:10:00

2017-12-17 17:01:23

限界上下文系統(tǒng)模型

2025-03-18 08:14:05

2023-07-11 10:02:23

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)