python之with语句的规律,Python中的上下文物管理理器

先发时间:2018-02-二叁 壹5:28

python之with语句的规律,pythonwith语句

头阵时间:201八-0二-二三 一伍:28


前面看来一篇博客说博主python面试时遇上面试官提问with的法则,而那位博主的博文未有提起with原理,故有此文。

 

 

contextlib — Context Manager Utilities

简言之,with 语句是超人的顺序块 “try catch
finally”的壹种格局抽取。python的我在PEP3四三中写道


有关with语句,官方文书档案中是如此讲述的:

The with statement is used to wrap the execution of a block with
methods defined by a context manager (see section With Statement Context
Managers). This allows common tryexceptfinally usage
patterns to be encapsulated for convenient reuse.

 with_stmt ::= "with" with_item ("," with_item)* ":" suite  with_item ::= expression ["as" target] 

The execution of the with statement with one “item” proceeds as follows:

The context expression (the expression given in the with_item) is
evaluated to obtain a context manager.

The context manager’s __exit__() is loaded for later use.

The context manager’s __enter__() method is invoked.

If a target was included in the with statement, the return value from
__enter__() is assigned to it.

Note
The with statement guarantees that if the __enter__() method returns
without an error, then __exit__() will always be called. Thus, if an
error occurs during the assignment to the target list, it will be
treated the same as an error occurring within the suite would be. See
step 6 below.

The suite is executed.

The context manager’s __exit__() method is invoked. If an exception
caused the suite to be exited, its type, value, and traceback are passed
as arguments to __exit__(). Otherwise, three None arguments are
supplied.

 

contextlib – 上下文物管理理器套件

Purpose: Utilities for creating and working with context managers.
The contextlib module contains utilities for working with context
managers and the with statement.
目标:
用于创制和采纳上下文管理器的套件。contextlib模块包涵用于采纳上下文物管理理器即with语句的套件。

Context Manager API
上下文物管理理器API

A context manager is responsible for a resource within a code block,
possibly creating it when the block is entered and then cleaning it up
after the block is exited. For example, files support the context
manager API to make it easy to ensure they are closed after all reading
or writing is done.
上下文物管理理器首要用以拍卖二个代码块中的能源:可能在进入代码块时创立,然后在代码块执行实现后清理。例如:如下所示的代码,使用援助上下文物管理理器API的文本打开格局将很轻易的管教在读写完结未来,文件都将被关闭。

contextlib_file.py

with open('/tmp/pymotw.txt', 'wt') as f:
    f.write('contents go here')

“ This PEP adds a new statement “with” to the Python language to make it
possible to factor out standard uses of try/finally statements.”

事先看来1篇博客说博主python面试时碰着面试官提问with的原理,而那位博主的博文未有谈到with原理,故有此文。

谷歌(谷歌(Google))翻译成普通话便是:

with语句用于选择由上下文物管理理器定义的措施来封装块的实践(请参见使用语句上下文物管理理器一节)。
那允许通用的try…except…finally使用形式被封装以便于重用【那句话大致意思正是“with语句”类似于try…except…finally封装之后的的情事】。

带有三个“项目”的with语句的履行进度如下:
一.上下文表明式(在with_item中提交的表达式)被评估以获得上下文物管理理器。【会分别种类来处理,如文件,进程等都能够利用with语句】
二.上下文物管理理器的__exit
__()被加载供之后采纳。【负责上下文的脱离】
叁.上下文物管理理器的__enter __()方法被调用。【负责上下文的进入】
肆.1旦在with语句中涵盖目的,则将__enter
__()的重临值分配给它。【固然with前边随着as 对象(如with open() as
f),那么此指标获得with上下文对象的__enter__()的重临值,(附:应该是相仿操作数据库时的连日对象和游标的分化)】

注意
with语句保证,如果__enter __()方法返回时没有错误,那么将始终调用__exit __()。 因此,如果在分配给目标列表期间发生错误,它将被视为与套件内发生的错误相同。 请参阅下面的第6步。

5.该套件已履行。【意思便是语句体中的进程举办达成,执行达成就到第五步–调用__exit__()来退出】
陆.上下文物管理理器的__exit __()方法被调用。
假诺那些导致套件退出,则其品种,值和追忆作为参数字传送递给__exit
__()。 不然,将提供四个无参数。

关于退出返回值:

If the suite was exited due to an exception, and the return value from the __exit__() method was false, the exception is reraised. If the return value was true, the exception is suppressed, and execution continues with the statement following the with statement.

If the suite was exited for any reason other than an exception, the return value from __exit__() is ignored, and execution proceeds at the normal location for the kind of exit that was taken.

中文:
如果套件由于异常而退出,并且__exit __()方法的返回值为false,则会重新对异常进行重新评估。 如果返回值为true,则异常被抑制,并继续执行with语句后面的语句。

如果套件由于除了异常之外的任何原因而退出,则__exit __()的返回值将被忽略,并且执行将在正常位置继续进行。

 

 

 

意思就是:

如果是异常退出,那么会返回false,(根据文档中的exit的描述“that __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.”,大概意思就是exit()不会处理异常,会重新抛出异常抛出给外面,由调用者处理,因为这是调用者的责任)

 

如果返回 True,则忽略异常,不再对异常进行处理【(在exit内部处理完异常后,可以让”__exit__()”方法返回True,此时该异常就会不会再被抛出,with会认为它的执行体没有发生异常)】

 

 

(with会识别重返值,依照再次来到值来处理,若是是False,那么with会将执行体中的极度抛出,若是是True,那么with会认为未有发生尤其(忽略万分),而继续执行外面包车型地铁语句,但出于个中调用的了__exit__(),所以在尤其之后的说话是不会运作的)

 

沾满1个文档中提供的二个有关with中动用锁的例子:

 

file is automatically closed

那是Python追求语言简化做出的一种语法糖。

 

多少个测试:

文本自动被关门

A context manager is enabled by the with statement, and the API involves
two methods. The enter() method is run when execution flow enters
the code block inside the with. It returns an object to be used within
the context. When execution flow leaves the with block, the exit()
method of the context manager is called to clean up any resources being
used.
上下文物管理理器能够由with语句开启,他的API包蕴三个章程:当程序进入with语句块时,就运维
美高梅开户网址,enter()方法。所重返的目的足以在上下文中使用。当执行语句流要离开with语句块时,调用上下文物管理理器的
exit()方法将清理所利用的一切财富。

contextlib_api.py

class Context:

    def __init__(self):
        print('__init__()')

    def __enter__(self):
        print('__enter__()')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__()')

with Context():
    print('Doing work in the context')

Combining a context manager and the with statement is a more compact way
of writing a try:finally block, since the context manager’s exit()
method is always called, even if an exception is raised.
将上下文物管理理器和with语句结合起来使用,比编写try:finally语句块显得更简洁,那是因为上下文物管理理器的
exit()方法会自动调用,甚至在现身极度的情况下。

  • python3 contextlib_api.py

__init__()
__enter__()
Doing work in the context
__exit__()

The enter() method can return any object to be associated with a
name specified in the as clause of the with statement. In this example,
the Context returns an object that uses the open context.
enter()方法能够回到任何在with语句中的as子句所钦命名称相关联的对象。
在本示例中,Context类重返二个应用打开上下文的目的。

contextlib_api_other_object.py

class WithinContext:

    def __init__(self, context):
        print('WithinContext.__init__({})'.format(context))

    def do_something(self):
        print('WithinContext.do_something()')

    def __del__(self):
        print('WithinContext.__del__')


class Context:

    def __init__(self):
        print('Context.__init__()')

    def __enter__(self):
        print('Context.__enter__()')
        return WithinContext(self)

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('Context.__exit__()')

with Context() as c:
    c.do_something()

The value associated with the variable c is the object returned by
enter(), which is not necessarily the Context instance created in
the with statement.
与变量c相关联的值是由enter()所再次来到的靶子,并不是必须在with语句中创造的Context实例。

$ python3 contextlib_api_other_object.py

Context.init()
Context.enter()
WithinContext.init(<main.Context object at 0x1007b1c50>)
WithinContext.do_something()
Context.exit()
WithinContext.del

The exit() method receives arguments containing details of any
exception raised in the with block.
exit()方法收到的参数,包涵with语句块中所抛出的别的特别新闻的底细。

contextlib_api_error.py

class Context:

    def __init__(self, handle_error):
        print('__init__({})'.format(handle_error))
        self.handle_error = handle_error

    def __enter__(self):
        print('__enter__()')
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__()')
        print('  exc_type =', exc_type)
        print('  exc_val  =', exc_val)
        print('  exc_tb   =', exc_tb)
        return self.handle_error

with Context(True):
    raise RuntimeError('error message handled')

print()

with Context(False):
    raise RuntimeError('error message propagated')

If the context manager can handle the exception, exit() should
return a true value to indicate that the exception does not need to be
propagated. Returning false causes the exception to be re-raised after
exit() returns.
假定上下文管理器可以处理卓殊,exit()将回来1个true,指明卓殊不必要被盛传。假如回去false将促成很是在
exit()在回来之后重新抛出。

$ python3 contextlib_api_error.py

init(True)
enterpython之with语句的规律,Python中的上下文物管理理器。()
exit()
exc_type = <class ‘RuntimeError’>
exc_val = error message handled
exc_tb = <traceback object at 0x10115cc88>

init(False)
enter()
exit()
exc_type = <class ‘RuntimeError’>
exc_val = error message propagated
exc_tb = <traceback object at 0x10115cc88>
Traceback (most recent call last):
File “contextlib_api_error.py”, line 33, in <module>
raise RuntimeError(‘error message propagated’)
RuntimeError: error message propagated

with 能够作用于类的某部方法,不过这么些类必须贯彻__enter__和 
__exit__多个法子。调用类的某个方法时,首先会调用__enter__办法,再调用方法自个儿,最终调用__exit__方法。

 

一.执行体中生出尤其:

import time
class myContextDemo(object):
    def __init__(self,gen):
        self.gen = gen
    def __enter__(self):
        print("enter in ")
        return self.gen
    def __exit__(self, exc_type, exc_val, exc_tb):
#exc_type是exception_type  exc_val是exception_value  exc_tb是exception_trackback
        print("exit in ")
        if exc_type is None:#如果是None 则继续执行
            print("None:",exc_type, exc_val, exc_tb)

        else: #异常不为空时执行,这一步,如果with语句体中发生异常,那么也会执行
            print("exception:", exc_type, exc_val, exc_tb)
            print("all done")



if __name__=="__main__":
    gen=(i for i in range(5,10))
    G=myContextDemo(gen)
    with G as f :
        print("hello")
        for i in f:
            print(i,end="\t")
        #测试1:执行体中发生异常
        raise Exception("母鸡啊")
    print("main continue")

结果突显:

壹.抛出拾叁分后,后面main continue不再执行

2.__exit__()中的else会执行

 

Context Managers as Function Decorators

诸如下边代码:

至于with语句,官方文书档案中是那般描述的:

The with statement is used to wrap the execution of a block with
methods defined by a context manager (see section With Statement Context
Managers). This allows common tryexceptfinally usage
patterns to be encapsulated for convenient reuse.

with_stmt ::= “with” with_item (“,” with_item)* “:” suite

with_item ::= expression [“as” target]

The execution of the with statement with one “item” proceeds as follows:

The context expression (the expression given in the with_item) is
evaluated to obtain a context manager.

The context manager’s __exit__() is loaded for later use.

The context manager’s __enter__() method is invoked.

If a target was included in the with statement, the return value from
__enter__() is assigned to it.

Note
The with statement guarantees that if the __enter__() method returns
without an error, then __exit__() will always be called. Thus, if an
error occurs during the assignment to the target list, it will be
treated the same as an error occurring within the suite would be. See
step 6 below.

The suite is executed.

The context manager’s __exit__() method is invoked. If an exception
caused the suite to be exited, its type, value, and traceback are passed
as arguments to __exit__(). Otherwise, three None arguments are
supplied.

 

测试二:当else中强制再次来到为True时:

 

import time
class myContextDemo(object):
    def __init__(self,gen):
        self.gen = gen
    def __enter__(self):
        print("enter in ")
        return self.gen
    def __exit__(self, exc_type, exc_val, exc_tb):
#exc_type是exception_type  exc_val是exception_value  exc_tb是exception_trackback
        print("exit in ")
        if exc_type is None:#如果是None 则继续执行
            print("None:",exc_type, exc_val, exc_tb)

        else: #异常不为空时执行,这一步,如果with语句体中发生异常,那么也会执行
            print("exception:", exc_type, exc_val, exc_tb)
            print("all done")
            return True #这里如果返回true可以看到发生异常后,main continue可以执行
            #即,如果exc_type是true,那么会继续执行,实际上,也可以在这里处理一下异常再返回true


if __name__=="__main__":
    gen=(i for i in range(5,10))
    G=myContextDemo(gen)
    with G as f :
        print("hello")
        for i in f:
            print(i,end="\t")
        raise Exception("母鸡啊")
        # print("continue")#这里不会执行
    print("main continue")

结果呈现:

壹.回去True之后,with会忽略相当,继续执行,所以那边“main continue”能实施

二.哪怕忽略极度,在with体中那几个之后的口舌依旧不会实施

附:理论上得以在回来True此前处理一下十一分

 

 

 

 

 

 

 

PS:就算大家想要了然得更详实,能够友善尝尝去读一下合法文书档案。

依附关于with语句的详细介绍官方文书档案:


先发时间:201八-02-2③ 1五:2八在此以前看到一篇博客说博主python面试时碰到面试官提问with的法则,而那位博主的…

上下文管理器作为函数装饰器

The class ContextDecorator adds support to regular context manager
classes to let them be used as function decorators as well as context
managers.
ContextDecorator类扩展了对于1般的上下文管理器类的支持,允许它们既作为函数装饰器,又作为上下文物管理理器使用。

contextlib_decorator.py

import contextlib

class Context(contextlib.ContextDecorator):

    def __init__(self, how_used):
        self.how_used = how_used
        print('__init__({})'.format(how_used))

    def __enter__(self):
        print('__enter__({})'.format(self.how_used))
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('__exit__({})'.format(self.how_used))


@Context('as decorator')
def func(message):
    print(message)

print()
with Context('as context manager'):
    print('Doing work in the context')

print()
func('Doing work in the wrapped function')

One difference with using the context manager as a decorator is that the
value returned by enter() is not available inside the function being
decorated, unlike when using with and as. Arguments passed to the
decorated function are available in the usual way.
运用上下文物管理理器作为装饰器的三个分歧在于,通过
enter()再次回到的值并不设有于被点缀的函数之中,那与运用with和as不一致。传入装饰函数的参数与往常同等存在。

$ python3 contextlib_decorator.py

init(as decorator)

init(as context manager)
enter(as context manager)
Doing work in the context
exit(as context manager)

enter(as decorator)
Doing work in the wrapped function
exit(as decorator)
From Generator to Context Manager

Creating context managers the traditional way, by writing a class with
enter() and exit() methods, is not difficult. But sometimes
writing everything out fully is extra overhead for a trivial bit of
context. In those sorts of situations, use the contextmanager()
decorator to convert a generator function into a context manager.
创立上下文物管理理器的思想意识艺术,正是通过在类中编辑 enter() 和
exit(),那并不困难。然而有时为了一点并不重要的上下文管理,就编写全体完好无缺的始末,显得毫不须要。在那类意况下,使用contextmanager()装饰器,将二个生成器函数转换为二个上下文物管理理器函数。

contextlib_contextmanager.py

import contextlib

@contextlib.contextmanager
def make_context():
    print('  entering')
    try:
        yield {}
    except RuntimeError as err:
        print('  ERROR:', err)
    finally:
        print('  exiting')

print('Normal:')
with make_context() as value:
    print('  inside with statement:', value)

print('\nHandled error:')
with make_context() as value:
    raise RuntimeError('showing example of handling an error')

print('\nUnhandled error:')
with make_context() as value:
    raise ValueError('this exception is not handled')

The generator should initialize the context, yield exactly one time,
then clean up the context. The value yielded, if any, is bound to the
variable in the as clause of the with statement. Exceptions from within
the with block are re-raised inside the generator, so they can be
handled there.
生成器函数将起先化上下文物管理理器函数,仅仅使用yield一回,然后清理上下文。假如有任何被yield的值,它们都被限制与with语句的as子句的变量之中。来自于with语句块的拾叁分会在生成器内部重新抛出,因而,它们能够在此被拍卖。

$ python3 contextlib_contextmanager.py

Normal:
  entering
  inside with statement: {}
  exiting

Handled error:
  entering
  ERROR: showing example of handling an error
  exiting

Unhandled error:
  entering
  exiting
Traceback (most recent call last):
  File "contextlib_contextmanager.py", line 32, in <module>
    raise ValueError('this exception is not handled')
ValueError: this exception is not handled

The context manager returned by contextmanager() is derived from
ContextDecorator, so it also works as a function decorator.
由contextmanager()重临的上下文物管理理器是由ContextDecorator衍生出的,由此它以函数装饰器的主意工作。

contextlib_contextmanager_decorator.py

import contextlib

@contextlib.contextmanager
def make_context():
    print('  entering')
    try:
        # Yield control, but not a value, because any value
        # yielded is not available when the context manager
        # is used as a decorator.
        yield
    except RuntimeError as err:
        print('  ERROR:', err)
    finally:
        print('  exiting')


@make_context()
def normal():
    print('  inside with statement')


@make_context()
def throw_error(err):
    raise err


print('Normal:')
normal()

print('\nHandled error:')
throw_error(RuntimeError('showing example of handling an error'))

print('\nUnhandled error:')
throw_error(ValueError('this exception is not handled'))

As in the ContextDecorator example above, when the context manager is
used as a decorator the value yielded by the generator is not available
inside the function being decorated. Arguments passed to the decorated
function are still available, as demonstrated by throw_error() in this
example.
相比以上ContextDecorator示例所展现,当context管理器当作装饰器使用时,通过生成器yield的值并不存在于棉被服装饰的函数之中。传递至装饰器函数的参数依旧有效,正如在本示例中通过throw_error()所体现的。

$ python3 contextlib_contextmanager_decorator.py

Normal:
  entering
  inside with statement
  exiting

Handled error:
  entering
  ERROR: showing example of handling an error
  exiting

Unhandled error:
  entering
  exiting
Traceback (most recent call last):
  File "contextlib_contextmanager_decorator.py", line 43, in
<module>
    throw_error(ValueError('this exception is not handled'))
  File ".../lib/python3.5/contextlib.py", line 30, in inner
    return func(\*args, \*\*kwds)
  File "contextlib_contextmanager_decorator.py", line 33, in
throw_error
    raise err
ValueError: this exception is not handled

Closing Open Handles
关闭打开操作

The file class supports the context manager API directly, but some other
objects that represent open handles do not. The example given in the
standard library documentation for contextlib is the object returned
from urllib.urlopen(). There are other legacy classes that use a close()
method but do not support the context manager API. To ensure that a
handle is closed, use closing() to create a context manager for it.
file类能够间接帮忙上下文物管理理器,可是1些表示打开文件操作的别的对象并不补助。本示例由专业库contextlib的文书档案所提供,是urllib.urlopen()所再次回到的对象。还有别的的残留代码的类,使用close()方法,不过并不帮忙上下文物管理理器API。想要确定保障句柄是倒闭的,为其接纳closing()来创制八个上下文物管理理器。

contextlib_closing.py

import contextlib


class Door:

    def __init__(self):
        print('  __init__()')
        self.status = 'open'

    def close(self):
        print('  close()')
        self.status = 'closed'

print('Normal Example:')
with contextlib.closing(Door()) as door:
    print('  inside with statement: {}'.format(door.status))
print('  outside with statement: {}'.format(door.status))

print('\nError handling example:')
try:
    with contextlib.closing(Door()) as door:
        print('  raising from inside with statement')
        raise RuntimeError('error message')
except Exception as err:
    print('  Had an error:', err)

The handle is closed whether there is an error in the with block or
not.
句柄都将被关闭,无论是还是不是在with语句中涵盖错误。

$ python3 contextlib_closing.py

Normal Example:
init()
inside with statement: open
close()
outside with statement: closed

Error handling example:
init()
raising from inside with statement
close()
Had an error: error message
Ignoring Exceptions

It is frequently useful to ignore exceptions raised by libraries,
because the error indicates that the desired state has already been
achieved, or it can otherwise be ignored. The most common way to ignore
exceptions is with a try:except statement with only a pass statement in
the except block.
见惯司空景况下忽略由库函数抛出的不胜新闻是很有不可或缺的,因为这几个错误申明了所企盼的意况已经高达,恐怕某种情状下能够被忽视。忽略卓殊最常用的法子就是利用try:except语句,而且在except语句块中只放入2个pass语句。

contextlib_ignore_error.py

import contextlib


class NonFatalError(Exception):
    pass


def non_idempotent_operation():
    raise NonFatalError(
        'The operation failed because of existing state'
    )


try:
    print('trying non-idempotent operation')
    non_idempotent_operation()
    print('succeeded!')
except NonFatalError:
    pass

print('done')

In this case, the operation fails and the error is ignored.
在这种气象下,操作战败,并且忽略错误。

$ python3 contextlib_ignore_error.py

trying non-idempotent operation

done

The try:except form can be replaced with contextlib.suppress() to more
explicitly suppress a class of exceptions happening anywhere in the with
block.
try:except情势能够选拔contextlib.suppress()替换,那样就足以更显式的平抑语句块中出现在任何岗位的足够。

contextlib_suppress.py

import contextlib


class NonFatalError(Exception):
    pass


def non_idempotent_operation():
    raise NonFatalError(
        'The operation failed because of existing state'
    )


with contextlib.suppress(NonFatalError):
    print('trying non-idempotent operation')
    non_idempotent_operation()
    print('succeeded!')

print('done')

In this updated version, the exception is discarded entirely.
在这么些立异的版本中,分外已经完全被撇下了。

$ python3 contextlib_suppress.py

trying non-idempotent operation
done

Redirecting Output Streams
重定向输出流

Poorly designed library code may write directly to sys.stdout or
sys.stderr, without providing arguments to configure different output
destinations. The redirect_stdout() and redirect_stderr() context
managers can be used to capture output from functions like this, for
which the source cannot be changed to accept a new output argument.
规划糟糕的库代码只怕回间接将出口写入sys.stdout或许sys.stderr,而并未有提供参数来布署不一致的出口形式。redirect_stdout()和redirect_stderr()上下文物管理理器可以被用来捕获类似于这样的函数输出:输入源不可能被改变来经受3个新的出口参数。

contextlib_redirect.py

from contextlib import redirect_stdout, redirect_stderr
import io
import sys


def misbehaving_function(a):
    sys.stdout.write('(stdout) A: {!r}\n'.format(a))
    sys.stderr.write('(stderr) A: {!r}\n'.format(a))


capture = io.StringIO()
with redirect_stdout(capture), redirect_stderr(capture):
    misbehaving_function(5)

print(capture.getvalue())

In this example, misbehaving_function() writes to both stdout and
stderr, but the two context managers send that output to the same
io.StringIO instance where it is saved to be used later.
在本示例中,misbehaving_function()同时写入stdout和stderr,不过四个上下文物管理理器将出口发送至同1个io.StringIO实例,保存以备后续使用。

$ python3 contextlib_redirect.py

(stdout) A: 5
(stderr) A: 5

Note
注意

Both redirect_stdout() and redirect_stderr() modify global state by
replacing objects in the sys module, and should be used with care. The
functions are not thread-safe, and may interfere with other operations
that expect the standard output streams to be attached to terminal
devices.
redirect_stdout()和redirect_stderr()函数通过覆盖sys模块中的对象来修改全局对象,使用时必定要一点都不大心。而且这一个函数不是线程安全,而且可能影响别的期望将业内输出流附加到终极的操作。

Dynamic Context Manager Stacks
动态上下文物管理理器栈

Most context managers operate on one object at a time, such as a single
file or database handle. In these cases, the object is known in advance
and the code using the context manager can be built around that one
object. In other cases, a program may need to create an unknown number
of objects in a context, while wanting all of them to be cleaned up when
control flow exits the context. ExitStack was created to handle these
more dynamic cases.
大部上下文物管理理器一次操作3个目的,例如三个文本或然三个数据库句柄。在这个意况下,对象是先期领会的,而且接纳上下文物管理理器的代码能够被用于基于1个对象营造;在任何情状下,程序可能须要在左右文中创制一定未知数量的目的,当程控流退出上下文时,就将富有的这么些目的清理。创立ExitStack处理那么些动态类型。

An ExitStack instance maintains a stack data structure of cleanup
callbacks. The callbacks are populated explicitly within the context,
and any registered callbacks are called in the reverse order when
control flow exits the context. The result is like having multple nested
with statements, except they are established dynamically.
ExitStack示例维护了三个清理回调函数当数码结构栈。回调函数显式的流入上下文,然后当控制流退出上下文时,全部已注册的回调函数依次依照逆序调用。结果就好像有多少个放置的说话,除非他们是全自动创设。

Stacking Context Managers
上下文物管理理器栈

There are several ways to populate the ExitStack. This example uses
enter_context() to add a new context manager to the stack.
由很种种格局为ExitStack填入数据,本示例使用enter_context()方法为栈添加3个新的上下文物管理理器。

contextlib_exitstack_enter_context.py

import contextlib


@contextlib.contextmanager
def make_context(i):
    print('{} entering'.format(i))
    yield {}
    print('{} exiting'.format(i))


def variable_stack(n, msg):
    with contextlib.ExitStack() as stack:
        for i in range(n):
            stack.enter_context(make_context(i))
        print(msg)


variable_stack(2, 'inside context')

enter_context() first calls enter() on the context manager, and
then registers its exit() method as a callback to be invoked as the
stack is undone.
enter_context()首先在上下文物管理理器上调用 enter(),然后将其注册到
exit() 方法上,作为1旦栈被打消,就调用的回调函数。

$ python3 contextlib_exitstack_enter_context.py

0 entering
1 entering
inside context
1 exiting
0 exiting

The context managers given to ExitStack are treated as though they are
in a series of nested with statements. Errors that happen anywhere
within the context propagate through the normal error handling of the
context managers. These context manager classes illustrate the way
errors propagate.
提需要ExitStack的上下文物管理理器,嵌套在一多重的with语句之中。当中上下文中任何岗位产生的谬误,都将透过正规错误处理的上下文物管理理器传播。那一个上下文管理器类体现了流传错误的不二等秘书诀。

contextlib_context_managers.py

import contextlib

class Tracker:
    "Base class for noisy context managers."

    def __init__(self, i):
        self.i = i

    def msg(self, s):
        print('  {}({}): {}'.format(
            self.__class__.__name__, self.i, s))

    def __enter__(self):
        self.msg('entering')


class HandleError(Tracker):
    "If an exception is received, treat it as handled."

    def __exit__(self, \*exc_details):
        received_exc = exc_details[1] is not None
        if received_exc:
            self.msg('handling exception {!r}'.format(
                exc_details[1]))
        self.msg('exiting {}'.format(received_exc))
        # Return Boolean value indicating whether the exception
        # was handled.
        return received_exc


class PassError(Tracker):
    "If an exception is received, propagate it."

    def __exit__(self, \*exc_details):
        received_exc = exc_details[1] is not None
        if received_exc:
            self.msg('passing exception {!r}'.format(
                exc_details[1]))
        self.msg('exiting')
        # Return False, indicating any exception was not handled.
        return False


class ErrorOnExit(Tracker):
    "Cause an exception."

    def __exit__(self, \*exc_details):
        self.msg('throwing error')
        raise RuntimeError('from {}'.format(self.i))


class ErrorOnEnter(Tracker):
    "Cause an exception."

    def __enter__(self):
        self.msg('throwing error on enter')
        raise RuntimeError('from {}'.format(self.i))

    def __exit__(self, \*exc_info):
        self.msg('exiting')

The examples using these classes are based around variable_stack(),
which uses the context managers passed to construct an ExitStack,
building up the overall context one by one. The examples below pass
different context managers to explore the error handling behavior.
First, the normal case of no exceptions.
本示例使用这么些类都以依照variable_stack()方法,该措施运用传入上下文管理器营造3个ExitStack,稳步创设壹体化的上下文。以下示例传递分歧的上下文物管理理器,来测试错误处理行为。首先传入未有极度的正规意况:

print('No errors:')
variable_stack([
    HandleError(1),
    PassError(2),
])

Then, an example of handling exceptions within the context managers at
the end of the stack, in which all of the open contexts are closed as
the stack is unwound.
下一场,使用上下文物管理理器处理栈末尾至极的演示,在该栈中,1旦栈展开,全体打开的上下文物管理理器都关闭。

print('\nError at the end of the context stack:')
variable_stack([
    HandleError(1),
    HandleError(2),
    ErrorOnExit(3),
])

Next, an example of handling exceptions within the context managers in
the middle of the stack, in which the error does not occur until some
contexts are already closed, so those contexts do not see the error.
下一步,使用上下文管理器处理栈中间极度的以身作则,在该栈中,直到上下文物管理理器已经关门,栈中的谬误都不会时有产生,由此,上下文管理器将看不到这么些错误。

print('\nError in the middle of the context stack:')
variable_stack([
    HandleError(1),
    PassError(2),
    ErrorOnExit(3),
    HandleError(4),
])

Finally, an example of the exception remaining unhandled and propagating
up to the calling code.
最终,遗留的那些代码未处理,并且传播到调用代码。

try:
    print('\nError ignored:')
    variable_stack([
        PassError(1),
        ErrorOnExit(2),
    ])
except RuntimeError:
    print('error handled outside of context')

If any context manager in the stack receives an exception and returns a
True value, it prevents that exception from propagating up to any other
context managers.
万1栈中的任何上下文物管理理器收到万分并且再次回到True,就截留了向任何上下文物管理理器传播非凡。

$ python3 contextlib_exitstack_enter_context_errors.py

No errors:
HandleError(1): entering
PassError(2): entering
PassError(2): exiting
HandleError(1): exiting False
outside of stack, any errors were handled

Error at the end of the context stack:
HandleError(1): entering
HandleError(2): entering
ErrorOnExit(3): entering
ErrorOnExit(3): throwing error
HandleError(2): handling exception RuntimeError(‘from 3’,)
HandleError(2): exiting True
HandleError(1): exiting False
outside of stack, any errors were handled

Error in the middle of the context stack:
HandleError(1): entering
PassError(2): entering
ErrorOnExit(3): entering
HandleError(4): entering
HandleError(4): exiting False
ErrorOnExit(3): throwing error
PassError(2): passing exception RuntimeError(‘from 3’,)
PassError(2): exiting
HandleError(1): handling exception RuntimeError(‘from 3’,)
HandleError(1): exiting True
outside of stack, any errors were handled

Error ignored:
PassError(1): entering
ErrorOnExit(2): entering
ErrorOnExit(2): throwing error
PassError(1): passing exception RuntimeError(‘from 2’,)
PassError(1): exiting
error handled outside of context
Arbitrary Context Callbacks

ExitStack also supports arbitrary callbacks for closing a context,
making it easy to clean up resources that are not controlled via a
context manager.
为了关闭一个上下文物管理理器,ExitStack
也辅助其余情势的回调,使不被上下文物管理理器控制的财富清理变的简练。

contextlib_exitstack_callbacks.py

import contextlib


def callback(\*args, \*\*kwds):
    print('closing callback({}, {})'.format(args, kwds))


with contextlib.ExitStack() as stack:
    stack.callback(callback, 'arg1', 'arg2')
    stack.callback(callback, arg3='val3')

Just as with the exit() methods of full context managers, the
callbacks are invoked in the reverse order that they are registered.
正如总体上下文物管理理器的 exit() 方法,回调函数以登记的逆序被调用。

$ python3 contextlib_exitstack_callbacks.py

closing callback((), {‘arg3’: ‘val3’})
closing callback((‘arg1’, ‘arg2’), {})

The callbacks are invoked regardless of whether an error occurred, and
they are not given any information about whether an error occurred.
Their return value is ignored.
不论是不是产生错误,都会调用回调函数,而且不会提供任何有关是不是有荒唐爆发的音讯,直接忽略再次回到值。

contextlib_exitstack_callbacks_error.py

import contextlib


def callback(\*args, \*\*kwds):
    print('closing callback({}, {})'.format(args, kwds))


try:
    with contextlib.ExitStack() as stack:
        stack.callback(callback, 'arg1', 'arg2')
        stack.callback(callback, arg3='val3')
        raise RuntimeError('thrown error')
except RuntimeError as err:
    print('ERROR: {}'.format(err))

Because they do not have access to the error, callbacks are unable to
suppress exceptions from propagating through the rest of the stack of
context managers.
鉴于未有接触到错误,回调函数就不能够抑制来自于通过上下文物管理理器别的栈传播的那些。

$ python3 contextlib_exitstack_callbacks_error.py

closing callback((), {‘arg3’: ‘val3’})
closing callback((‘arg1’, ‘arg2’), {})
ERROR: thrown error

Callbacks make a convenient way to clearly define cleanup logic without
the overhead of creating a new context manager class. To improve code
readability, that logic can be encapsulated in an inline function, and
callback() can be used as a decorator.
回调函数使定义清理函数逻辑分外清晰并且有利于,而且防止了创立叁个新的上下文物管理理器类的费用。想要证南齐码的可读性,能够将逻辑封装在二个内嵌函数中,将回调函数作为装饰器。

contextlib_exitstack_callbacks_decorator.py

import contextlib

with contextlib.ExitStack() as stack:

    @stack.callback
    def inline_cleanup():
        print('inline_cleanup()')
        print('local_resource = {!r}'.format(local_resource))

    local_resource = 'resource created in context'
    print('within the context')

There is no way to specify the arguments for functions registered using
the decorator form of callback(). However, if the cleanup callback is
defined inline, scope rules give it access to variables defined in the
calling code.
对于使用装饰器情势的callback()函数注册的函数未有主意钦点参数。不过,假若清理回调函数以内嵌的诀要定义,功用域管理将可以予以访问在调用代码中定义变量的权限。

$ python3 contextlib_exitstack_callbacks_decorator.py

within the context
inline_cleanup()
local_resource = ‘resource created in context’
Partial Stacks

Sometimes when building complex contexts it is useful to be able to
abort an operation if the context cannot be completely constructed, but
to delay the cleanup of all resources until a later time if they can all
be set up properly. For example, if an operation needs several
long-lived network connections, it may be best to not start the
operation if one connection fails. However, if all of the connections
can be opened they need to stay open longer than the duration of a
single context manager. The pop_all() method of ExitStack can be used
in this scenario.
偶然,当营造复杂上下文时,假诺上下文未有完好的创设,退出机制就足够实用。可是假诺能够百分之百稳当的配备好,直到后续有个别时刻再清理全数的能源。例如:假使操作须要供给某些长日子的互联网连接,1旦某八个接连退步,就最为不用开始操作。然则,假如持有的接二连三都能开拓,那么连接就必要的打开时间比单个上下文物管理理器的生命周期要长。ExitStack的pop_all()方法能够在如此的场景下使用。

pop_all() clears all of the context managers and callbacks from the
stack on which it is called, and returns a new stack pre-populated with
those same context managers and callbacks. The close() method of the new
stack can be invoked later, after the original stack is gone, to clean
up the resources.
pop_all()函数清理了栈中所调用的富有上下文管理器和回调函数,然后再次来到贰个新的栈,栈中预先填入了那一个相同的上下文物管理理器和回调函数。新栈的close()方法能够继承被调用,初始的栈被清空后,就将回收全数的能源。

contextlib_exitstack_pop_all.py

import contextlib

from contextlib_context_managers import *


def variable_stack(contexts):
    with contextlib.ExitStack() as stack:
        for c in contexts:
            stack.enter_context(c)
        # Return the close() method of a new stack as a clean-up
        # function.
        return stack.pop_all().close
    # Explicitly return None, indicating that the ExitStack could
    # not be initialized cleanly but that cleanup has already
    # occurred.
    return None


print('No errors:')
cleaner = variable_stack([
    HandleError(1),
    HandleError(2),
])
cleaner()

print('\nHandled error building context manager stack:')
try:
    cleaner = variable_stack([
        HandleError(1),
        ErrorOnEnter(2),
    ])
except RuntimeError as err:
    print('caught error {}'.format(err))
else:
    if cleaner is not None:
        cleaner()
    else:
        print('no cleaner returned')

print('\nUnhandled error building context manager stack:')
try:
    cleaner = variable_stack([
        PassError(1),
        ErrorOnEnter(2),
    ])
except RuntimeError as err:
    print('caught error {}'.format(err))
else:
    if cleaner is not None:
        cleaner()
    else:
        print('no cleaner returned')

This example uses the same context manager classes defined earlier, with
the difference that ErrorOnEnter produces an error on enter()
instead of exit(). Inside variable_stack(), if all of the contexts
are entered without error then the close() method of a new ExitStack is
returned. If a handled error occurs, variable_stack() returns None to
indicate that the cleanup work is already done. And if an unhandled
error occurs, the partial stack is cleaned up and the error is
propagated.
本示例使用此前定义的同壹的上下文物管理理器类,使用不一样的 ErrorOnEnter
生成一个在 enter() 的错误,而不是
exit()。在variable_stack()内部,假如进入全数的上下文物管理理器,而且从不错误,就再次回到新的ExitStack中的close()方法。假诺发生错误,variable_stack()就再次来到None,表秦代理工科作早已成功。固然有怎么着未处理的不当产生,栈的壹有的就被清理,并且将错误抛出

$ python3 contextlib_exitstack_pop_all.py

No errors:
HandleError(1): entering
HandleError(2): entering
HandleError(2): exiting False
HandleError(1): exiting False

Handled error building context manager stack:
HandleError(1): entering
ErrorOnEnter(2): throwing error on enter
HandleError(1): handling exception RuntimeError(‘from 2’,)
HandleError(1): exiting True
no cleaner returned

Unhandled error building context manager stack:
PassError(1): entering
ErrorOnEnter(2): throwing error on enter
PassError(1): passing exception RuntimeError(‘from 2’,)
PassError(1): exiting
caught error from 2

with file(r'D:\a.txt','r') as f :
    print f.read()

谷歌(谷歌)翻译成粤语正是:

with语句用于采纳由上下文物管理理器定义的主意来封装块的施行(请参见使用语句上下文管理器1节)。
那允许通用的try…except…finally使用形式被封装以便于重用【那句话大约意思就是“with语句”类似于try…except…finally封装之后的的景况】。

富含1个“项目”的with语句的施行进度如下:
一.上下文表明式(在with_item中提交的表达式)被评估以赢得上下文管理器。【会分别种类来拍卖,如文件,进度等都能够选用with语句】
二.上下文物管理理器的__exit
__()被加载供现在使用。【负责上下文的脱离】
3.上下文物管理理器的__enter __()方法被调用。【负责上下文的进去】
4.假诺在with语句中包罗指标,则将__enter
__()的再次来到值分配给它。【假如with前面跟着as 对象(如with open() as
f),那么此目的得到with上下文对象的__enter__()的重返值,(附:应该是近似操作数据库时的连接对象和游标的分别)】

注意
with语句保证,假如__enter
__()方法重返时没有不当,那么将始终调用__exit __()。
因而,如若在分配给指标列表时期发生错误,它将被视为与套件内产生的谬误相同。
请参阅上边包车型地铁第四步。

伍.该套件已施行。【意思便是语句体中的进程执行完毕,执行完成就到第4步–调用__exit__()来退出】
6.上下文物管理理器的__exit __()方法被调用。
要是不行导致套件退出,则其体系,值和纪念作为参数字传送递给__exit
__()。 不然,将提供多个无参数。

关于退出返回值:

If the suite was exited due to an exception, and the return value from the __exit__() method was false, the exception is reraised. If the return value was true, the exception is suppressed, and execution continues with the statement following the with statement.

If the suite was exited for any reason other than an exception, the return value from __exit__() is ignored, and execution proceeds at the normal location for the kind of exit that was taken.

中文:
如果套件由于异常而退出,并且__exit __()方法的返回值为false,则会重新对异常进行重新评估。 如果返回值为true,则异常被抑制,并继续执行with语句后面的语句。

如果套件由于除了异常之外的任何原因而退出,则__exit __()的返回值将被忽略,并且执行将在正常位置继续进行。

 

 

 

意思就是:

如果是异常退出,那么会返回false,(根据文档中的exit的描述“that __exit__() methods should not reraise the passed-in exception; this is the caller’s responsibility.”,大概意思就是exit()不会处理异常,会重新抛出异常抛出给外面,由调用者处理,因为这是调用者的责任)

 

如果返回 True,则忽略异常,不再对异常进行处理【(在exit内部处理完异常后,可以让”__exit__()”方法返回True,此时该异常就会不会再被抛出,with会认为它的执行体没有发生异常)】

 

 

(with会识别重返值,根据重回值来处理,假若是False,那么with会将执行体中的十分抛出,假设是True,那么with会认为未有发生尤其(忽略相当),而继续执行外面包车型地铁语句,但出于内部调用的了__exit__(),所以在尤其之后的讲话是不会运作的)

 

沾满四个文书档案中提供的三个关于with中动用锁的例子:

美高梅开户网址 1

 

如此那般,大家兑现了读取a.txt全体内容。读取后又关闭了文本。

多少个测试:

咱俩得以查看file方法所在的__builtin_模块,你能够寻找对应file类到__enter__和__exit__的不二诀窍(这几个事例不佳,因为file是c达成的,那里唯有壳,但能协助你掌握是何许看头)

一.执行体中产生特别:

import time
class myContextDemo(object):
    def __init__(self,gen):
        self.gen = gen
    def __enter__(self):
        print("enter in ")
        return self.gen
    def __exit__(self, exc_type, exc_val, exc_tb):
#exc_type是exception_type  exc_val是exception_value  exc_tb是exception_trackback
        print("exit in ")
        if exc_type is None:#如果是None 则继续执行
            print("None:",exc_type, exc_val, exc_tb)

        else: #异常不为空时执行,这一步,如果with语句体中发生异常,那么也会执行
            print("exception:", exc_type, exc_val, exc_tb)
            print("all done")



if __name__=="__main__":
    gen=(i for i in range(5,10))
    G=myContextDemo(gen)
    with G as f :
        print("hello")
        for i in f:
            print(i,end="\t")
        #测试1:执行体中发生异常
        raise Exception("母鸡啊")
    print("main continue")

结果呈现:美高梅开户网址 2

壹.抛出万分后,后边main continue不再执行

2.__exit__()中的else会执行

 

 

测试2:当else中恐吓再次来到为True时:

 

import time
class myContextDemo(object):
    def __init__(self,gen):
        self.gen = gen
    def __enter__(self):
        print("enter in ")
        return self.gen
    def __exit__(self, exc_type, exc_val, exc_tb):
#exc_type是exception_type  exc_val是exception_value  exc_tb是exception_trackback
        print("exit in ")
        if exc_type is None:#如果是None 则继续执行
            print("None:",exc_type, exc_val, exc_tb)

        else: #异常不为空时执行,这一步,如果with语句体中发生异常,那么也会执行
            print("exception:", exc_type, exc_val, exc_tb)
            print("all done")
            return True #这里如果返回true可以看到发生异常后,main continue可以执行
            #即,如果exc_type是true,那么会继续执行,实际上,也可以在这里处理一下异常再返回true


if __name__=="__main__":
    gen=(i for i in range(5,10))
    G=myContextDemo(gen)
    with G as f :
        print("hello")
        for i in f:
            print(i,end="\t")
        raise Exception("母鸡啊")
        # print("continue")#这里不会执行
    print("main continue")

结果展现:美高梅开户网址 3

一.赶回True之后,with会忽略非常,继续执行,所以这里“main continue”能举行

二.固然忽略分外,在with体中充裕之后的语句依旧不会举行

附:理论上得以在回到True从前处理一下特别

 

 

 

 

 

 

 

PS:若是大家想要明白得更详实,能够协调尝试去读一下合法文书档案。

屈居关于with语句的事无巨细介绍官方文书档案:


上边出自python的合法文档,详细表达了with的用法。with还援助嵌套。

 

with_stmt ::=  "with" with_item ("," with_item)* ":" suite
with_item ::=  expression ["as" target]
  1. The context expression (the expression given in the with_item)
    is evaluated to obtain a context manager.

  2. The context manager’s __exit__() is loaded for later use.

  3. The context manager’s __enter__() method is invoked.

  4. If a target was included in the with statement, the return value
    from __enter__() is assigned to it.

    Note

     
    The with statement guarantees that if
    the __enter__() method returns without an
    error, then __exit__() will always be called. Thus,
    if an error occurs during the assignment to the target list, it will
    be treated the same as an error occurring within the suite would be.
    See step 6 below.

  5. The suite is executed.

  6. The context manager’s __exit__() method is invoked. If an
    exception caused the suite to be exited, its type, value, and
    traceback are passed as arguments to __exit__().
    Otherwise, three Nonearguments
    are supplied.

    If the suite was exited due to an exception, and the return value
    from the __exit__() method was false, the
    exception is reraised. If the return value was true, the exception
    is suppressed, and execution continues with the statement following
    the with statement.

    If the suite was exited for any reason other than an exception, the
    return value from __exit__() is ignored, and execution
    proceeds at the normal location for the kind of exit that was taken.

With more than one item, the context managers are processed as if
multiple with statements were nested:

with A() as a, B() as b:
    suite

is equivalent to

with A() as a:
    with B() as b:
        suite

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图