with语句适用于对资源进行访问的场合,确保不管使用过程中是否发生异常都会执行必要的清理工作,来释放资源,比如文件使用后自动关闭,县城中锁的自动获取和释放等。
with相关的术语:
要是用with语句,首先要明白上下文管理器这一概念。有了上下文管理器,with语句才能工作。
下面是一组与上下文管理器和with语句相关的概念。
上下文管理协议(Context Managerment Protocol):包含enter()和exit()方法,支持该协议的对象要实现这两个方法。
上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了enter()和exit()方法。上下文管理器定义执行with语句时要建立的运行时上下文,负责执行with语句块上下文中的进入与退出操作。通常使用with语句调用上下文管理器,也可以通过直接调用其方法来使用。
运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的enter()和exit()方法实现,enter()方法在语句执行之前进入运行时上下文,exit()在语句体执行完后从运行时上下文退出。with语句支持运行时上下文这一概念。
上下文表达式(context Expression):with语句中跟在关键字with之后的表达式,该表达式要返回一个上下文管理器对象。
语句体(with-body):with语句包裹起来的代码块,在执行语句之前会调用上下文管理器的enter()方法,执行完语句体之后会执行exit()方法。
基本语法和工作原理:
with语句的语法格式如下:
1. with语句的语法格式
with context_expression [as target(s)]:
with-body
这里context expression要返回一个上下文管理器对象,该对象并不赋值给as子语句中的target,如果指定了as子句的话,会将上下文管理器中的_enter()方法的返回值赋值给target.
target可以是单个变量,或者由"()"括起来的元组(不能是仅仅由“,”分割的变量列表,必须加“()”)
在oslo_utils中的timeutils中的应用
在\nova\nova\compute\manager.py
from oslo_utils import timeutils
在manager.py中使用timeutils的例子如下:
with timeutils.StopWatch() as timer:
self._build_and_run_instance(context, instance, image,
decoded_files, admin_password, requested_networks,
security_groups, block_device_mapping, node, limits,
filter_properties, request_spec, accel_uuids)
LOG.info('Took %0.2f seconds to build instance.',
timer.elapsed(), instance=instance)
这里通过timer.elapsed()来显示timeutils.StopWatch()到LOG.info之间的时间
oslo_utils中的StopWatch的实现如下:
https://github.com/openstack/oslo.utils/blob/master/oslo_utils/timeutils.py
class StopWatch(object):
_STARTED = 'STARTED'
_STOPPED = 'STOPPED'
def __init__(self, duration = None):
if duration is not None and duration < 0:
raise ValueError()
self._duration = duration
self._started_at = None
self._stopped_at = None
self._state = None
self._split = ()
#开始计数
def start(self):
if self._state == self._STARTED:
return self
self._started_at = now()
self._stopped_at = None
#当前状态设置为_Started
self._state = self._STARTED
self._splits = ()
@staticmethod
def _delta_seconds(earlier, later):
return max(0, later - earlier)
def elapsed(self, maximum = None)
if self._state not in (self._STARTED, self._STOPPED):
raise()
if self._state == self._STOPPED:
elapsed = self._delta_seconds(self._started_at, self._stopped_at)
else:
elapsed = self._delta_seconds(self._started_at, now())
if maximum is not None and elapsed > maximum:
elapsed = max(0.0, maximum)
return elapsed
#通过with 调用StopWatch的时候,会自动调用__enter__,在__enter__ 中有会调用start 来开始计数
def __enter__(self):
"""Starts the watch."""
self.start()
return self
StopWatch 这个class 有实现__enter__,因此当在with 中调用StopWatch 是会自动调用__enter__,在__enter__ 中有会调用自身的start函数 来记录下当前的时间,当前时间是通过now函数获得,并把时间记录到内部变量_started_at中这样当用户下次在主动调用elapsed的时候就会主动 通过now函数得到当前的时间减去之前记录在_started_at的时间.