標籤:函數式編程 python進階with
一:起因
(0)Python的基本文法,對於一個學過其他語言的人來說,比較容易;但是要是熟練的應用 和 掌握Python的進階文法還是有一段路要走的。
(1)With語句代替try……finally語句;yield文法之產生器generator,序列產生器;函數式編程(Map/Reduce/Filter等 ps:這裡的Map/Reduce不是Hadoop的MR)
(3)樣本請詳見
二:With基本文法
(0)要說With文法,首先講一講 上下文管理器
舉個例子,你在寫Python代碼的時候經常將一系列操作放在一個語句塊中:
當某條件為真 – 執行這個語句塊;當某條件為真 – 迴圈執行這個語句塊;有時候我們需要在當程式在語句塊中運行時保持某種狀態,並且在離開語句塊後結束這種狀態。
所以,事實上上下文管理器的任務是 – 代碼塊執行前準備,代碼塊執行後收拾。
看代碼是最好的學習方式,來看看我們通常是如何開啟一個檔案並寫入”Hello World”?
filename = 'my_file.txt'mode = 'w' # Mode that allows to write to the filewriter = open(filename, mode)writer.write('Hello ')writer.write('World')writer.close()
1-2行,我們指明檔案名稱以及開啟檔案(寫入)。
第3行,開啟檔案,4-5行寫入“Hello world”,第6行關閉檔案。
這樣不就行了,為什麼還需要上下文管理器?但是我們忽略了一個很小但是很重要的細節:如果我們沒有機會到達第6行關閉檔案,那會怎樣?
舉個例子,磁碟已滿,因此我們在第4行嘗試寫入檔案時就會拋出異常,而第6行則根本沒有機會執行。
當然,我們可以使用try-finally語句塊來進行封裝:
writer = open(filename, mode)try: writer.write('Hello ') writer.write('World')finally: writer.close()
finally語句塊中的代碼無論try語句塊中發生了什麼都會執行。因此可以保證檔案一定會關閉。這麼做有什麼問題嗎?當然沒有,但當我們進行一些比寫入“Hello world”更複雜的
事情時,try-finally語句就會變得醜陋無比。例如我們要開啟兩個檔案,一個讀一個寫,兩個檔案之間進行拷貝操作,那麼通過with語句能夠保證兩者能夠同時被關閉。
(1)with 語句的文法格式如下:
with context_expression [as target(s)]: with-body
這裡 context_expression 要返回一個上下文管理器對象,該對象並不賦值給 as 子句中的 target(s) ,如果指定了 as 子句的話,會將上下文管理器的 __enter__() 方法的返
回值賦值給 target(s)。target(s) 可以是單個變數,或者由“()”括起來的元組(不能是僅僅由“,”分隔的變數列表,必須加“()”)。
(2)Python 對一些內建對象進行改進,加入了對上下文管理器的支援,可以用於 with 語句中,比如可以自動關閉檔案、線程鎖的自動擷取和釋放等。假設要對一個檔案進行
操作,使用 with 語句可以有如下代碼:使用 with 語句操作檔案對象
with open(r'somefileName') as somefile: for line in somefile: print line # ...more code
這裡使用了 with 語句,不管在處理檔案過程中是否發生異常,都能保證 with 語句執行完畢後已經關閉了開啟的檔案控制代碼。
(3)如果使用傳統的 try/finally 範式,則要使用類似如下代碼:(用於和with的對比)
somefile = open(r'somefileName') try: for line in somefile: print line # ...more code finally: somefile.close()
比較起來,使用 with 語句可以減少編碼量。已經加入對上下文管理協議支援的還有模組 threading、decimal 等。
三:執行個體說明
(1)with 語句的執行過程類似如下代碼塊:
context_manager = context_expression exit = type(context_manager).__exit__ value = type(context_manager).__enter__(context_manager) exc = True # True 表示正常執行,即便有異常也忽略;False 表示重新拋出異常,需要對異常進行處理 try: try: target = value # 如果使用了 as 子句 with-body # 執行 with-body except: # 執行過程中有異常發生 exc = False # 如果 __exit__ 返回 True,則異常被忽略;如果返回 False,則重新拋出異常 # 由外層代碼對異常進行處理 if not exit(context_manager, *sys.exc_info()): raise finally: # 正常退出,或者通過 statement-body 中的 break/continue/return 語句退出 # 或者忽略異常退出 if exc: exit(context_manager, None, None, None) # 預設返回 None,None 在布爾上下文中看做是 False
執行 context_expression,產生上下文管理器 context_manager
調用上下文管理器的 __enter__() 方法;如果使用了 as 子句,則將 __enter__() 方法的返回值賦值給 as 子句中的 target(s)
執行語句體 with-body
不管是否執行過程中是否發生了異常,執行內容管理器的 __exit__() 方法,__exit__() 方法負責執行“清理”工作,如釋放資源等。如果執行過程中沒有出現異常,或者語句體中執行了語句 break/continue/return,則以 None 作為參數調用 __exit__(None, None, None) ;如果執行過程中出現異常,則使用 sys.exc_info 得到的異常資訊為參數調用 __exit__(exc_type, exc_value, exc_traceback)
出現異常時,如果 __exit__(type, value, traceback) 返回 False,則會重新拋出異常,讓with 之外的語句邏輯來處理異常,這也是通用做法;如果返回 True,則忽略異常,不再對異常進行處理
(2)另外python庫中還有一個模組contextlib,使你不用構造含有__enter__, __exit__的類就可以使用with:(更加實用了)
>>> from contextlib import contextmanager>>> from __future__ import with_statement>>> @contextmanager... def context():... print 'entering the zone'... try:... yield... except Exception, e:... print 'with an error %s'%e... raise e... else:... print 'with no error'...>>> with context():... print '----in context call------'...entering the zone----in context call------with no error
(3)@property 可以將python定義的函數“當做”屬性訪問,從而提供更加友好訪問方式,但是有時候setter/getter也是需要的
class Parrot: def __init__(self): self._voltage = 100000 @property def voltage(self): """Get the current voltage.""" return self._voltageif __name__ == "__main__": # instance p = Parrot() # similarly invoke "getter" via @property print p.voltage # update, similarly invoke "setter" p.voltage = 12
Python進階With文法