Python上下文管理器與with語句

什么是上下文管理器

上下文管理器顧名思義是管理上下文的,也就是負責沖鋒和墊后,而讓主人專心完成自己的事情秩命。我們在編寫程序的時候袄友,通常會將一系列操作放到一個語句塊中殿托,當某一條件為真時執(zhí)行該語句快碌尔。有時候,我們需要再執(zhí)行一個語句塊時保持某種狀態(tài)叹坦,并且在離開語句塊后結(jié)束這種狀態(tài)。例如對文件的操作莹捡,我們在打開一個文件進行讀寫操作時需要保持文件處于打開狀態(tài),而等操作完成之后要將文件關(guān)閉。所以寥茫,上下文管理器的任務(wù)是:代碼塊執(zhí)行前準備,代碼塊執(zhí)行后收拾膝迎。上下文管理器是在Python2.5加入的功能,它能夠讓你的代碼可讀性更強并且錯誤更少费尽。

需求的產(chǎn)生

在正常的管理各種系統(tǒng)資源(文件、鎖定和連接)柏卤,在涉及到異常時通常是個棘手的問題敌蚜。異常很可能導(dǎo)致控制流跳過負責釋放關(guān)鍵資源的語句齐媒。例如打開一個文件進行操作時,如果意外情況發(fā)生(磁盤已滿、特殊的終端信號讓其終止等),就會拋出異常,這樣可能最后的文件關(guān)閉操作就不會執(zhí)行。如果這樣的問題頻繁出現(xiàn)眯分,則可能耗盡系統(tǒng)資源。

是的,這樣的問題并不是不可避免。在沒有接觸到上下文管理器之前净响,我們可以用“try/finally”語句來解決這樣的問題掸掸〔淝铮或許在有些人看來扰付,“try/finally”語句顯得有些繁瑣仁讨。上下文管理器就是被設(shè)計用來簡化“try/finally”語句的,這樣可以讓程序更加簡潔盐固。

With語句

With語句用于執(zhí)行上下文操作,它也是復(fù)合語句的一種,其基本語法如下所示:

with context_expr [as var]:
    with_suite

With 語句僅能工作于支持上下文管理協(xié)議(context management protocol)的對象渔隶。也就是說只有內(nèi)建了"上下文管理"的對象才能和 with 一起工作。Python內(nèi)置了一些支持該協(xié)議的對象婉弹,如下所列是一個簡短列表:

  • file
  • decimal.Context
  • thread.LockType
  • threading.Lock
  • threading.RLock
  • threading.Condition
  • threading.Semaphore
  • threading.BoundedSemaphore

由以上列表可以看出睬魂,file 是已經(jīng)內(nèi)置了對上下文管理協(xié)議的支持。所以我們可以用下邊的方法來操作文件:

with open('/etc/passwd', 'r') as f:
    for eachLine in f:
        # ...do stuff with eachLine or f...

上邊的代碼試圖打開一個文件,如果一切正常,把文件對象賦值給 f商佛。然后用迭代器遍歷文件中的每一行,當 完成時,關(guān)閉文件喉钢。無論是在這一段代碼的開始,中間,還是結(jié)束時發(fā)生異常,會執(zhí)行清理的代碼,此 外文件仍會被自動的關(guān)閉。

自定義上下文管理器

要實現(xiàn)上下文管理器良姆,必須實現(xiàn)兩個方法:一個負責進入語句塊的準備操作肠虽,另一個負責離開語句塊的善后操作。Python類包含兩個特殊的方法玛追,分別名為:__enter____exit__税课。

  • enter: 該方法進入運行時上下文環(huán)境,并返回自身或另一個與運行時上下文相關(guān)的對象痊剖。返回值會賦給 as 從句后面的變量韩玩,as 從句是可選的。
  • exit: 該方法退出當前運行時上下文并返回一個布爾值陆馁,該布爾值標明了“如果 with_suit 的退出是由異常引發(fā)的找颓,該異常是否須要被忽略”。如果 exit() 的返回值等于 False叮贩,那么這個異常將被重新引發(fā)一次击狮;如果 exit() 的返回值等于 True,那么這個異常就被無視掉益老,繼續(xù)執(zhí)行后面的代碼彪蓬。

With 語句的實際執(zhí)行流程是這樣的:

1. 執(zhí)行 context_exp 以獲取上下文管理器
2. 加載上下文管理器的 exit() 方法以備稍后調(diào)用
3. 調(diào)用上下文管理器的 enter() 方法
4. 如果有 as var 從句,則將 enter() 方法的返回值賦給 var
5. 執(zhí)行子代碼塊 with_suit
6. 調(diào)用上下文管理器的 exit() 方法捺萌,如果 with_suit 的退出是由異常引發(fā)的寞焙,那么該異常的 type、value 和 traceback 會作為參數(shù)傳給 exit()互婿,否則傳三個 None
7. 如果 with_suit 的退出由異常引發(fā),并且 exit() 的返回值等于 False辽狈,那么這個異常將被重新引發(fā)一次慈参;如果 exit() 的返回值等于 True,那么這個異常就被無視掉刮萌,繼續(xù)執(zhí)行后面的代碼

下面我們自己來實現(xiàn)一個支持上下文管理協(xié)議的文件操作:

#! /usr/bin/env python
# -*- coding: utf-8 -*-

# *************************************************************
#     Filename @  contextfile.py
#       Author @  Huoty
#  Create date @  2015-08-08 17:02:13
#  Description @  
# *************************************************************

filename = 'my_file.txt'
mode = 'w' # Mode that allows to write to the file
writer = open(filename, mode)

class PypixOpen(object):
    def __init__(self, filename, mode):
        self.filename = filename
        self.mode = mode

    def __enter__(self):
        self.openedFile = open(self.filename, self.mode)
        return self.openedFile

    def __exit__(self, *unused):
        self.openedFile.close()

# Script starts from here

with PypixOpen(filename, mode) as writer:
    writer.write("Hello World from our new Context Manager!")

更加優(yōu)雅的上下文管理(contextlib模塊)

contextlib模塊提供更易用的上下文管理器驮配。

contextlib.closing

contextlib.closing 方法在語句塊結(jié)束后調(diào)用對象的 close 方法。

from contextlib import closing
import urllib

with closing(urllib.urlopen('http://www.python.org')) as page:
    for line in page:
        print line

contextlib.nested

contextlib.nested 方法用于替換嵌套的 with 語句。例如壮锻,有兩個文件琐旁,一個讀一個寫,即進行拷貝猜绣。以下是不提倡的用法:

with open('toReadFile', 'r') as reader:
    with open('toWriteFile', 'w') as writer:
        writer.writer(reader.read())

這里可以用 contextlib.nested 進行優(yōu)化:

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

contextlib.contextmanager

contextlib.contextmanager 是一個裝飾器灰殴,它可以用來裝飾被 yield 語句分割成兩部分的函數(shù),以此進行上下文管理掰邢。任何在yield之前的內(nèi)容都可以看做在代碼塊執(zhí)行前的操作牺陶,而任何yield之后的操作都可以看做是代碼塊結(jié)束后要做的操作。如果希望在上下文管理器中使用 “as” 關(guān)鍵字辣之,那么就用 yield 返回你需要的值掰伸,它將通過 as 關(guān)鍵字賦值給新的變量。

from contextlib import contextmanager

@contextmanager
def tag(name):
    print "<%s>" % name
    yield
    print "</%s>" % name

使用 contextlib.contextmanager 時怀估,可以大致套用如下的框架:

from contextlib import contextmanager

@contextmanager
def closing(thing):
    try:
        yield thing
    finally:
        thing.close()
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末狮鸭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子多搀,更是在濱河造成了極大的恐慌歧蕉,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件酗昼,死亡現(xiàn)場離奇詭異廊谓,居然都是意外死亡,警方通過查閱死者的電腦和手機麻削,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門蒸痹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呛哟,你說我怎么就攤上這事叠荠。” “怎么了扫责?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵榛鼎,是天一觀的道長。 經(jīng)常有香客問我鳖孤,道長者娱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任苏揣,我火速辦了婚禮黄鳍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘平匈。我一直安慰自己框沟,他們只是感情好藏古,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著忍燥,像睡著了一般拧晕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上梅垄,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天厂捞,我揣著相機與錄音,去河邊找鬼哎甲。 笑死蔫敲,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的炭玫。 我是一名探鬼主播奈嘿,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吞加!你這毒婦竟也來了裙犹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤衔憨,失蹤者是張志新(化名)和其女友劉穎叶圃,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體践图,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡掺冠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了码党。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片德崭。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖揖盘,靈堂內(nèi)的尸體忽然破棺而出眉厨,到底是詐尸還是另有隱情,我是刑警寧澤兽狭,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布憾股,位于F島的核電站,受9級特大地震影響箕慧,放射性物質(zhì)發(fā)生泄漏服球。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一颠焦、第九天 我趴在偏房一處隱蔽的房頂上張望斩熊。 院中可真熱鬧,春花似錦蒸健、人聲如沸座享。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渣叛。三九已至,卻和暖如春盯捌,著一層夾襖步出監(jiān)牢的瞬間淳衙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工饺著, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留箫攀,地道東北人。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓幼衰,卻偏偏與公主長得像靴跛,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子渡嚣,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容

  • contextlib — Context Manager Utilities contextlib - 上下文管理...
    英武閱讀 2,818評論 0 52
  • 轉(zhuǎn)載自:http://mp.weixin.qq.com/s/LO1yyFeUA6pR_YPyfDoSig 姓名:梅...
    虐先森閱讀 1,424評論 0 1
  • 引言 with 語句是從 Python 2.5 開始引入的一種與異常處理相關(guān)的功能(2.5 版本中要通過 from...
    氨基鈉閱讀 350評論 0 2
  • 首先梢睛,什么是上下文管理器?上下文管理器就是實現(xiàn)了上下文管理協(xié)議的對象识椰。主要用于保存和恢復(fù)各種全局狀態(tài)绝葡,關(guān)閉文件等,...
    王吉吉real閱讀 12,728評論 0 6
  • 本文轉(zhuǎn)自淺談Python的with語句 引言 with 語句是從 Python 2.5 開始引入的一種與異常處理相...
    Syfun閱讀 4,008評論 0 50