上下文
上下文的实现一般可以通过装饰器或者上下文管理器实现,装饰器确保函数可以运行在正确的上下文中,或者在函数前后运行一些代码。
上下文装饰器
当一个数据项需要在多个线程之间共享时,就要用一个锁来保护它避免多次访问。这个锁可以在装饰器中编写(当然也可以不使用修饰器),代码如下:
from threading import RLock
lock = RLock()def synchronized(function):def _synchronized(*args, **kw):lock.acquire()try:return function(*args, **kw)finally:lock.release()return _synchronized@synchronized
def thread_safe(): # 确保锁定资源pass
关于修饰器的构建本系列的文章已经介绍过了,不再赘述,在代码中创建了一个互斥锁,这里是将函数调用作为临界区,防止被多次调用。关于互斥锁的使用暂时不必深究,只需要知道他是一种线程共享的方式即可。
上下文管理器
为了确保即使在出现错误的情况下也能运行某些清理代码,try…finally语句是很有用的。如关闭文件、释放锁、创建临时代码补丁等功能。
with语句为这些使用场景下的代码块包装提供了一种简单方法。即使该代码块引发了异常,也可以在其执行前后调用一些代码。
with语句
我们熟悉的文件打开方式就是如此,假设我们有一个名为:test.txt的文件,打开过程可以是以下方式打开:
with open("test.txt") as f:# 可以在这里进行对文件的操作pass
上面打开方式的优点就是不需要调用close对文件进行关闭,保证了文件随用随关。
创建一个上下文管理器
任何实现了上下文管理器协议(context manager protocol)的对象都可以用作上下文管理器。该协议包含两个特殊方法。
- __ enter __ (self):
- __ exit __ (self, exc _ type, exc _ value, traceback):
简而言之,执行with语句的过程如下:
- 调用__ enter __方法。任何返回值都会绑定到指定的as子句。
- 执行内部代码块。
- 调用__ exit __方法。
__ exit __
接受代码块中出现错误时填入的3个参数。如果没有出现错误,那么这3个参数都被设为None。
出现错误时,__ exit__不会重新引发这个错误,但它可以通过返回True来避免引发异常。
下面是某个实现了这一协议的上下文管理器示例
class ContextIllustration:def __enter__(self):print('entering context')def __exit__(self, exc_type, exc_value, traceback):print('leaving context')if exc_type is None:print('with no error')else:print('with an error (%s)' % exc_value)
在这里enter就是来凑数的,我们主要来看exit的部分
当遇到报错时三个参数不为None,所以只需要判断其中一个参数就可以得知是否有报错了。其中exc_type是异常值类型, exc_value是异常值。
正常调用未引起报错的情况如下:
with ContextIllustration():print("inside")
运行结果:
entering context
inside
leaving context
with no error
为了营造错误使用的情况,我们这里直接抛出错误
entering context
leaving context
with an error (raised within 'with')