1. 程式人生 > >python contextlib 上下文管理器

python contextlib 上下文管理器

sel 退出 named con 我們 file rac none ace

1、with操作符

在python中讀寫文件,可能需要這樣的代碼

try-finally讀寫文件

file_text = None
try:
    file_text = open(./text, r)
    print file_text.read()
except IOError, ex:
    traceback.print_exc()
finally:
    if file_text:
        file_text.close()

同樣,在python中使用線程鎖,可能需要這樣的代碼

try-finally線程鎖

lock = threading.Lock()
lock.acquire()
try: pass except Exception, ex: traceback.print_exc() finally: lock.release()

可能你會覺得這種寫法很不方便,python提供了with操作符,你可以這樣操作

with讀寫文件

with open(./text, r) as file_text:
    print file_text.read()

with線程鎖

with lock:
    pass

是不是方便多了。

其實,不只是lock和file可以使用with操作符。

實際上,任何對象,只要正確實現上下文管理,就可以使用with語句。實現上下文管理是通過 __enter__ 和 __exit__ 這兩個方法實現的。

2、上下文管理

上下文管理可以為我們屏蔽上下文的復雜性。例如,我們實現一個類Cat,實現其__enter__和__exit__方法。

__enter__(self): 進入上下文管理器時調用此方法,其返回值將被放入with-as語句中as說明符指定的變量中。

__exit__(self,type,value,tb):離開上下文管理器調用此方法。如果有異常出現,type、value、tb分別為異常的類型、值和追蹤信息。如果沒有異常,

3個參數均設為None。此方法返回值為True或者False,分別指示被引發的異常得到了還是沒有得到處理。如果返回False,引發的異常會被傳遞出上下文。

如下。

class Cat(object):

    def __init__(self, name):
        self.name = name

    def __enter__(self):
        print enter from Cat named %s % self.name
        return self

    def hello(self):
        print hello, %s % self.name

    def __exit__(self, exc_type, exc_val, exc_tb):
        print exit from Cat named %s % self.name

執行,並打印結果

with Cat(Tom) as tom:
    tom.hello()

enter from Cat named Tom
hello, Tom
exit from Cat named Tom

這裏我們執行as tom獲得了Cat類的一個實例,這是通過__enter__方法的返回得到的。

當然,我們可以管理多個,請註意進入和退出的順序。

with Cat(Tom) as tom, Cat(Harry) as harry:
    tom.hello()
    harry.hello()

enter from Cat named Tom
enter from Cat named Harry
hello, Tom
hello, Harry
exit from Cat named Harry
exit from Cat named Tom

3、contextmanager

可能你還是覺得實現__enter__和__exit__很麻煩。python提供了contextlib.contextmanager

讓我們重寫上面的例子,使用contextmanager

from contextlib import contextmanager as _contextmanager


@_contextmanager
def cat(name):
    print enter cat named %s % name
    yield name
    print exit cat named %s % name

執行,並打印結果

with cat(Kitty) as kitty:
    print hello, %s % kitty

enter cat named Kitty
hello, Kitty
exit cat named Kitty

as後面的實例,是通過yield語句返回的。這裏是返回了一個字符串。

當然,同樣支持管理多個實例

with cat(Kitty) as kitty, cat(Tom) as tom:
    print hello, %s % kitty
    print hello, %s % tom

enter cat named Kitty
enter cat named Tom
hello, Kitty
hello, Tom
exit cat named Tom
exit cat named Kitty

4、最後給出一個實例

使用上線文管理器實現redis分布式鎖

import redis
import time
import threading
import traceback
from contextlib import contextmanager as _contextmanager

r = redis.Redis(host=localhost, port=6379, db=0)

@_contextmanager
def dist_lock(client, key):
    dist_lock_key = lock:%s % key
    try:
        _acquire_lock(client, dist_lock_key)
        yield
        _release_lock(client, dist_lock_key)
    except Exception, ex:
        pass

def _acquire_lock(client, key):
    is_lock = r.set(key, 1, nx=True, ex=10)
    if not is_lock:
        raise Exception("already locked!")


def _release_lock(client, key):
    client.delete(key)


def func():
    while 1:
        try:
            with dist_lock(r, "key"):
                print "*"
                time.sleep(8)
        except Exception, ex:
            pass

thread_list = list()
for i in range(10):
    thread_list.append(threading.Thread(target=func))
for thread in thread_list:
    thread.start()
for thread in thread_list:
    thread.join()

python contextlib 上下文管理器