python高级特性_详解Python 高级特性(三)

上下文管理器

上下文管理器是可以在with语句中使用,拥有__enter__和__exit__方法的对象。

with manager as var:

do_something(var)

相当于以下情况的简化:

var = manager.__enter__()

try:

do_something(var)

finally:

manager.__exit__()

换言之,PEP 343中定义的上下文管理器协议允许将无聊的try...except...finally结构抽象到一个单独的类中,仅仅留下关注的do_something部分。

__enter__方法首先被调用。它可以返回赋给var的值。as部分是可选的:如果它不出现,enter的返回值简单地被忽略。

with语句下的代码被执行。就像try子句,它们或者成功执行到底,或者break,continue或return,或者可以抛出异常。无论哪种情况,该块结束后,__exit__方法被调用。如果抛出异常,异常信息被传递给__exit__,这将在下一章节讨论。通常情况下,异常可被忽略,就像在finally子句中一样,并且将在__exit__结束后重新抛出。

比如说我们想确认一个文件在完成写操作之后被立即关闭:

>>> class closing(object):

... def __init__(self, obj):

... self.obj = obj

... def __enter__(self):

... return self.obj

... def __exit__(self, *args):

... self.obj.close()

>>> with closing(open('/tmp/file', 'w')) as f:

... f.write('the contents\\n')

这里我们确保了当with块退出时调用了f.close()。因为关闭文件是非常常见的操作,该支持已经出现在file类之中。它有一个__exit__方法调用close,并且本身可作为上下文管理器。

>>> with open('/tmp/file', 'a') as f:

... f.write('more contents\\n')

try...finally常见的用法是释放资源。各种不同的情况实现相似:在__enter__阶段资源被获得,在__exit__阶段释放,如果抛出异常也被传递。正如文件操作,往往这是对象使用后的自然操作,内置支持使之很方便。每一个版本,Python都在更多的地方提供支持。

所有类似文件的对象:

file ➔ 自动关闭

fileinput,tempfile(py >= 3.2)

bz2.BZ2File,gzip.GzipFile,

tarfile.TarFile,zipfile.ZipFile

ftplib, nntplib ➔ 关闭连接(py >= 3.2)

multiprocessing.RLock ➔ 锁定和解锁

multiprocessing.Semaphore

memoryview ➔ 自动释放(py >= 3.2 或 3.3)

decimal.localcontext➔ 暂时更改计算精度

_winreg.PyHKEY ➔ 打开和关闭Hive Key

warnings.catch_warnings ➔ 暂时杀死(kill)警告

contextlib.closing ➔ 如上例,调用close

并行编程

concurrent.futures.ThreadPoolExecutor ➔

并行调用然后杀掉线程池(py >= 3.2)

concurrent.futures.ProcessPoolExecutor ➔

并行调用并杀死进程池(py >= 3.2)

nogil ➔ 暂时解决GIL问题(仅仅cyphon :()

捕获异常

当一个异常在with块中抛出时,它作为参数传递给__exit__。三个参数被使用,和sys.exc_info()返回的相同:类型、值和回溯(traceback)。当没有异常抛出时,三个参数都是None。上下文管理器可以通过从__exit__返回一个真(True)值来“吞下”异常。例外可以轻易忽略,因为如果__exit__不使用return直接结束,返回None——一个假(False)值,之后在__exit__结束后重新抛出。

捕获异常的能力创造了有意思的可能性。一个来自单元测试的经典例子——我们想确保一些代码抛出正确种类的异常:

class assert_raises(object):

# based on pytest and unittest.TestCase

def __init__(self, type):

self.type = type

def __enter__(self):

pass

def __exit__(self, type, value, traceback):

if type is None:

raise AssertionError('exception expected')

if issubclass(type, self.type):

return True # swallow the expected exception

raise AssertionError('wrong exception type')

with assert_raises(KeyError):

{}['foo']

使用生成器定义上下文管理器

当讨论生成器时,据说我们相比实现为类的迭代器更倾向于生成器,因为它们更短小方便,状态被局部保存而非实例和变量中。另一方面,正如双向通信章节描述的那样,生成器和它的调用者之间的数据流可以是双向的。包括异常,可以直接传递给生成器。我们想将上下文管理器实现为特殊的生成器函数。事实上,生成器协议被设计成支持这个用例。

@contextlib.contextmanager

def some_generator():

try:

yield

finally:

contextlib.contextmanager装饰一个生成器并转换为上下文管理器。生成器必须遵循一些被包装(wrapper)函数强制执行的法则——最重要的是它至少yield一次。yield之前的部分从__enter__执行,上下文管理器中的代码块当生成器停在yield时执行,剩下的在__exit__中执行。如果异常被抛出,解释器通过__exit__的参数将之传递给包装函数,包装函数于是在yield语句处抛出异常。通过使用生成器,上下文管理器变得更短小精炼。

让我们用生成器重写closing的例子:

@contextlib.contextmanager

def closing(obj):

try:

yield obj

finally:

obj.close()

再把assert_raises改写成生成器:

@contextlib.contextmanager

def assert_raises(type):

try:

yield

except type:

return

except Exception as value:

raise AssertionError('wrong exception type')

else:

raise AssertionError('exception expected')

这里我们用装饰器将生成函数转化为上下文管理器!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值