python上下文管理,with語句

今天在網上看到一段代碼,其中使用了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)
相關文章
相關標籤/搜索