您的位置:首页 > 编程语言 > Python开发

python contextlib.py

2015-12-16 17:16 555 查看
"""Utilities for with-statement contexts. See PEP 343."""

import sys

from functools import wraps

from warnings import warn

#这个模块定义了三个类:contextmanager、nested、closing

#其中比较常用的是contextmanager和closing

__all__ = ["contextmanager", "nested", "closing"]

class GeneratorContextManager(object):

"""Helper for @contextmanager decorator."""

#类实例化完成后调用此方法完成一些初始化工作

#很明显,我们实例化此类时需要传递一个generator作为其初始化参数

def __init__(self, gen):

self.gen = gen

#如果我们自己去实现一个上下文管理器,那么我们必须显示实现对应的__enter__和__exit__方法

#从后文我们可以知道,我们使用一个generator来初始化了本类

#而本类的意图也是在__enter__和__exit__中将generator中yield的前后两部分代码分别放在__enter__和__exit__中

#分别完成初始化操作(setup)和清理操作(cleanup)

def __enter__(self):

try:

#这里return值就是generator中yield后面跟随的值,也是我们在with...as variable中返回给variable的值

return self.gen.next()

except StopIteration:

raise RuntimeError("generator didn't yield")

#这里是执行yield后面的代码

#一般而言,我们的generator具有如下格式:

"""

<setup>

try:

yield <value>

finally:

<cleanup>

"""

#必须以generatorFunction().next()方式去驱动此generator执行,next()方法执行的分界点就是yield语句,yield后面跟随的变量作为next()方法的返回值

#第一次我们在__enter__中执行第一次next()方法,那么generator中<setup>部分代码开始运行,next()方法执行到yield后暂停返回

#当我们第二次执行generator.next()时,继续从第一次所遇到的yield处向下执行,如果到程序结尾仍没有发现yield语句,那么会raise StopIteration(此行为是我们所期望的)

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

if type is None:

#这里我们捕捉我们所期望的StopIteration异常

try:

#相当于执行generator的finally后面的语句

self.gen.next()

except StopIteration:

return

else:

raise RuntimeError("generator didn't stop")

else:

if value is None:

# Need to force instantiation so we can reliably

# tell if we get the same exception back

value = type()

try:

self.gen.throw(type, value, traceback)

raise RuntimeError("generator didn't stop after throw()")

except StopIteration, exc:

# Suppress the exception *unless* it's the same exception that

# was passed to throw(). This prevents a StopIteration

# raised inside the "with" statement from being suppressed

return exc is not value

except:

# only re-raise if it's *not* the exception that was

# passed to throw(), because __exit__() must not raise

# an exception unless __exit__() itself failed. But throw()

# has to raise the exception to signal propagation, so this

# fixes the impedance mismatch between the throw() protocol

# and the __exit__() protocol.

#

if sys.exc_info()[1] is not value:

raise

#这是一个装饰器函数,这个装饰器函数用来装饰我们的generator,在装饰器返回的新的函数中用我们的generator去初始化GeneratorContextManager

#最终在with语句中,执行装饰器返回的新函数,新函数的返回值是我们用我们定义的generator初始化的GeneratorContextManager对象

#这个对象具有__enter__、__exit__方法(实现上下文管理器)

#在__enter__中,我们返回给as后的variable值为原generator的yield后跟随的变量,此后我们用这个句柄去执行操作

#在__exit__中,我们执行对应的清理操作。(例如执行句柄的关闭操作)

def contextmanager(func):

"""@contextmanager decorator.

Typical usage:

#下面揭示了本module的用法--用装饰器装饰我们的generator,将我们的generator作为参数去初始化那么最终我们generator的返回值是一个上下文管理器,我们所要操作的句柄则返回给varable

@contextmanager

def some_generator(<arguments>):

<setup>

try:

yield <value>

finally:

<cleanup>

This makes this:

with some_generator(<arguments>) as <variable>:

<body>

equivalent to this:

<setup>

try:

<variable> = <value>

<body>

finally:

<cleanup>

"""

@wraps(func)

def helper(*args, **kwds):

return GeneratorContextManager(func(*args, **kwds))

return helper

@contextmanager

def nested(*managers):

"""Combine multiple context managers into a single nested context manager.

This function has been deprecated in favour of the multiple manager form

of the with statement.

The one advantage of this function over the multiple manager form of the

with statement is that argument unpacking allows it to be

used with a variable number of context managers as follows:

with nested(*managers):

do_something()

"""

warn("With-statements now directly support multiple context managers",

DeprecationWarning, 3)

exits = []

vars = []

exc = (None, None, None)

try:

for mgr in managers:

exit = mgr.__exit__

enter = mgr.__enter__

vars.append(enter())

exits.append(exit)

yield vars

except:

exc = sys.exc_info()

finally:

while exits:

exit = exits.pop()

try:

if exit(*exc):

exc = (None, None, None)

except:

exc = sys.exc_info()

if exc != (None, None, None):

# Don't rely on sys.exc_info() still containing

# the right information. Another exception may

# have been raised and caught by an exit method

raise exc[0], exc[1], exc[2]

#直接将某个句柄fd对象作为初始化参数传递

#在__enter__中返回这个句柄给as后的variable变量用于后续操作

#在离开with语句时,在__exit__中执行清理工作(例如句柄的关闭等)

class closing(object):

"""Context to automatically close something at the end of a block.

Code like this:

with closing(<module>.open(<arguments>)) as f:

<block>

is equivalent to this:

f = <module>.open(<arguments>)

try:

<block>

finally:

f.close()

"""

def __init__(self, thing):

self.thing = thing

def __enter__(self):

return self.thing

def __exit__(self, *exc_info):

self.thing.close()

这里我们仿照上面的写一个简单的contextlib.py的实现:

class contextManager:
def __init__(self, conn):
self.conn = conn
def __enter__(self):
return self.conn.next()
def __exit__(self, num1, num2, num3):
try:
self.conn.next()
except StopIteration:
pass
return False

def wrapper(func):
def innerwrapper():
conncm = contextManager(func())
return conncm
return innerwrapper

@wrapper
def conn():
print 'start...to....connection...'
yield
print 'stop...connection'

with conn() as conn_cm:
print 'do_something'


执行的结果是:

start...to....connection...
do_something
stop...connection
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: