Python中的上下文管理器和else塊

導語:本文章記錄了本人在學習Python基礎之控制流程篇的重點知識及我的心得,打算入門Python的朋友們能夠來一塊兒學習並交流。

本文重點:python

一、掌握if語句以外的else的用法;
二、掌握上下文管理器的定義、協議、使用和with塊;
三、掌握有用的@contextmanger裝飾器。

1、if語句以外的else塊

一、else塊介紹

if/else中if和else是同級對立的語句,對立是指流程通過一層if/else語句只能對應一種處理語句。而else在for/else,while/else,try/else語句中的功能則大相徑庭。後者中的else功能以下:數據庫

  • for/else:for循環沒有被break語句停止才運行else塊。
  • while/else:while循環沒有被break語句停止才運行else塊。
  • try/else:try塊中,沒有異常拋出時才運行else塊。

下面以for/else爲例進行代碼實現:編程

for i in "apple":
    if i.isupper():
        break
else:
    raise ValueError('No upper string was found')

二、try/else塊背後的編程風格

try/except不只用於處理錯誤,還用於處理錯誤,這屬於EAFP編程風格。app

  • EAFP:easier to ask for forgiveness than permission
    取得原諒比得到許可容易。即先假定存在有效的鍵或屬性,若是假定不成立,那麼捕獲異常。
  • LBYL:look before you leap
    三思然後行。即在調用函數或查找屬性或鍵以前顯式測試前提條件。

2、上下文管理器和with塊

一、上下文管理器介紹

上下文管理器(context manger):在操做文件和創建數據庫鏈接的時候,咱們最終須要關閉資源,這就是上下文管理器存在的意義。
上下文管理器協議:包含__enter__和__exit__兩個方法。
語法:try/finally模式和with語句
實例:try/finally模式函數

try:
    f = open('test.txt', 'a+')
    f.write('Foo\n')
finally:
    f.close()

接下來咱們用with語句進行替換:
實例:with語句工具

with open('test.txt', 'a+') as f:
    f.write('Foo\n')

分析:open 的返回值賦值給變量 f,當離開 with 代碼塊的時候,系統會自動調用 f.close() 方法。
總結:with塊的功能在於簡化try/finally模式。with語句在開始運行時會在上下文管理器對象上調用__enter__,而with語句結束時會調用__exit__方法。
Tips:with語句中的as語句是可選的,as語句將__enter__返回的值綁定到as語句後的變量。值得注意的是,對於open函數必須加上as字句來獲取文件的引用。學習

二、自定義上下文管理器

在掌握基本上下文管理器和with語句後,咱們經過自定義上下文管理器來深入認識with語句和__enter__以及__exit__的聯繫。
實例:自定義知足上下文管理器協議的類測試

class OpenFileDemo(object):
    def __init__(self, filename):
        self.filename = filename
 
        self.f = open(self.filename, 'a+')
        return self.f
 
    def __exit__(self, exc_type, exc_value, traceback):
        self.f.close()
        if exc_type != SyntaxError:
            return True
       return False

with OpenFileDemo('test.txt') as f:
    f.write('Foo\n')

三、異常處理

當上下文管理器遇到異常時由__exit__方法處理。傳給__exit__方法的三個參數以下:
exc_type:異常類(例如SyntaxError)。
exc_value:異常實例。有時會有參數傳給異常構造方法,例如錯誤信息,可使用exc_value.args獲取這些參數。
traceback:traceback對象。code

3、@contextmanger裝飾器

@contextmanger裝飾器是contextlib模塊中的工具,它能夠將包含yield的語句變成上下文管理器。
其中,yield以前的語句在__enter__方法中執行yield以後的語句在__exit__方法執行,yield後面的值是函數的返回值,綁定到實際調用的with中的as子句的目標變量上
如此能夠避免編寫一個類來實現上下文管理器協議。對象

實例:@contextmanger裝飾器應用之計時器

import contextlib
import time

@contextlib.contextmanager
def timer():
    start=time.time()
    yield
    end=time.time()
    usedtime=end-start
    print('Running time was %r seconds'%usedtime)

with timer() as usedtime:
    time.sleep(1)

注意:一旦with塊在調用timer出現異常時,拋出的異常會在timer函數中的yield表達式中再次拋出。若是timer函數沒有處理異常的代碼就會致使函數運行停止,系統處於無效狀態。所以必要時在上下文管理器函數中使用try/finally語句防範錯誤。

@contextmanger集合了三個不一樣的Python特性:函數裝飾器、生成器和with語句,很是實用!

4、contextlib模塊中的實用工具

最後說明contextlib模塊中包含的實用工具:

  • closing: 若是對象提供了 close() 方法,但沒有實現 _enter__/__exit_ 協議,那麼可使用這個函數構建上下文管理器。
  • suppress: 構建臨時忽略指定異常的上下文管理器。
  • @contextmanager: 這個裝飾器把簡單的生成器函數變成上下文管理器,這樣就不用建立類去實現管理器協議了。
  • ContextDecorator: 這是個基類,用於定義基於類的上下文管理器。這種上下文管理器也能用於裝飾函數,在受管理的上下文中運行整個函數。
  • ExitStack: 這個上下文管理器能進入多個上下文管理器。with 塊結束時,ExitStack 按照後進先出的順序調用棧中各個上下文管理器的_exit_ 方法。若是事先不知道 with 塊要進入多少個上下文管理器,可使用這個類。例如,同時打開任意一個文件列表中的全部文件。

顯然,在這些實用工具中,使用最普遍的是 @contextmanager 裝飾器,所以要格外留心。這個裝飾器也有迷惑人的一面,由於它與迭代無關,卻要使用 yield 語句。

相關文章
相關標籤/搜索