python contextlib 上下文管理器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了python contextlib 上下文管理器相关的知识,希望对你有一定的参考价值。

 

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 上下文管理器的主要内容,如果未能解决你的问题,请参考以下文章

Python3标准库:contextlib上下文管理器工具

Python:contextlib模块——上下文管理器工具

python contextlib 上下文管理器

python上下文管理器ContextLib及with语句

Python学习笔记__12.8章 contextlib

Python3之 contextlib