今天在網上看到一段代碼,其中使用了with seam:初見不解其意,遂查詢資料.python
代碼:數據庫
1 #! /usr/bin/env python 2 # -*- coding:utf-8 -*- 3 import time 4 from random import random 5 from threading import Thread,Semaphore 6 7 sema = Semaphore(3) 8 9 def foo(tid): 10 with sema: 11 print("{} acquire sema".format(tid)) 12 wt = random() * 2 13 time.sleep(wt) 14 print("{} release sema".format(tid)) 15 16 threads = [] 17 18 for i in range(5): 19 t = Thread(target=foo,args=(i,)) 20 threads.append(t) 21 t.start() 22 23 for t in threads: 24 t.join()
查詢 python核心編程第二版,其中有個章節,專門介紹了with語句和上下文管理.編程
with語句,在Python2.6正式啓用,和try-except-finally 相似,with語句也是用來簡化代碼的.Python中,並不是任意符號均可以使用with語句,with語句僅僅工做於支持上下文管理協議的對象.app
with應用場景:打開文件,日誌,數據庫,線程資源,簡單同步,數據庫鏈接等等.dom
1 with context_expr [as var]: ##context_expr:上下文表達式,[]可選 2 with_suite
能和with一塊兒使用的成員列表:ui
1 file 2 decimal.Context 3 thread.LockType 4 threading.Lock 5 threading.RLock 6 threading.Condition 7 threading.Semaphore 8 threading.BoundedSemaphore
最最多見的with用法:spa
1 with open("file","r") as f:
for line in f: 2 pass
上面的代碼都作了什麼操做?線程
程序試圖打開一個文本,若是一切正常,把文本對象賦值給f,而後用迭代器遍歷文本中的每一行.當完成時,關閉文本.日誌
不管在這段代碼的開始,中間,仍是結束時發生了異常,都會執行清理的代碼,此外文件仍然被會自動的關閉.code
先上一個最簡單的代碼:
1 class Context: 2 def __init__(self,name): 3 self.name = name 4 def __enter__(self): 5 print("Begin.__enter__") 6 return self 7 def __exit__(self, exc_type, exc_val, exc_tb): 8 print("End.__exit__") 9 def context(self): 10 print("This is context ...{}".format(self.name)) 11 12 with Context("xurui") as context: ##若是帶上 as 變量,那麼__enter__()方法必須得返回一個東西,要否則會報錯.. 13 context.context() 14 with Context("xurui"): 15 Context("xurui").context()
演示代碼:
class Sample: """ ##執行__enter__方法,它將完成with語句塊執行前的全部準備工做,若是with xx 後面帶上參數,as val,那麼__enter__返回值將賦值給 val,不然,丟棄返回值""" def __enter__(self): print("In __enter__()") return "Foo" ##返回"Foo"給as Sample中的Sample def __exit__(self, exc_type, exc_val, exc_tb): print("In __exit__()") def get_sample(): print(type(Sample())) return Sample() with get_sample() as Sample: print("sample:{}".format(Sample)) 結果: In __enter__() sample:Foo In __exit__()
但這都不是with的牛逼功能,with最強的地方,仍是用來處理異常...
__exit__(),有三個參數,類型(異常類),值(異常實例),和回溯(回溯對象).
演示代碼:
class Sample: """ ##執行__enter__方法,它將完成with語句塊執行前的全部準備工做,若是with xx 後面帶上參數,as val,那麼__enter__返回值將賦值給 val,不然,丟棄返回值""" def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): print("type:{},\nvalue:{},\ntrace:{}".format(exc_type,exc_val,exc_tb)) def do_something(self): bar = 1 / 0 return bar + 10 with Sample() as Sample: Sample.do_something() print("sample:{}".format(Sample)) 結果: type:<class 'ZeroDivisionError'>, value:division by zero, trace:<traceback object at 0x0000000001074CC8> Traceback (most recent call last): File "C:/Users/xurui/PycharmProjects/q1/2017-03-01/error.py", line 55, in <module> Sample.do_something() File "C:/Users/xurui/PycharmProjects/q1/2017-03-01/error.py", line 51, in do_something bar = 1 / 0 ZeroDivisionError: division by zero
1 import queue 2 import contextlib 3 4 5 @contextlib.contextmanager 6 def worker_state(xxx, val): 7 xxx.append(val) 8 # print(xxx) 9 try: 10 yield 11 finally: 12 xxx.remove(val) 13 # print(xxx) 14 15 16 q = queue.Queue() 17 li = [] 18 q.put("xurui") 19 with worker_state(li, 1): 20 print("before", li) 21 q.get() 22 print("after", li)
自定義一個open文件
1 import contextlib 2 3 4 @contextlib.contextmanager 5 def MyOpen(filename, mode): 6 try: 7 f = open(filename, mode, encoding='utf') 8 except Exception as e: 9 pass 10 else: 11 yield f ##f return 給 as f的f 12 finally: 13 f.close() 14 15 16 with MyOpen("1.py", 'r') as f: 17 ret = f.readlines() 18 print(ret)