“with”语句在Python中做什么?

What does the 'with' statement do in python?

本问题已经有最佳答案,请猛点这里访问。

我是Python的新手。在一个关于连接到MySQL和获取数据的教程中,我看到了with语句。我读到它,它与try-finally块有关。但我找不到一个我能理解的更简单的解释。


with语句打开一个资源,并保证当with块完成时,无论该块如何完成,都将关闭该资源。考虑一个文件:

1
2
3
4
with open('/etc/passwd', 'r') as f:
    print f.readlines()

print"file is now closed!"

即使您有一个return,即使您引发异常,文件也保证在块的末尾关闭。

为了使with作出此保证,表达式(示例中的open()必须是上下文管理器。好消息是,许多Python表达式都是上下文管理器,但不是全部。

根据我发现的一个教程,MySQLdb.connect()实际上是一个上下文管理器。

此代码:

1
2
3
4
5
conn = MySQLdb.connect(...)
with conn:
    cur = conn.cursor()
    cur.do_this()
    cur.do_that()

将作为单个事务提交或回滚命令序列。这意味着您不必太担心异常或其他异常代码路径——不管您如何离开代码块,事务都将得到处理。


从根本上讲,它是一个对象,它用在入口和出口调用的自定义逻辑来划分代码块,并且可以在其构造中接受参数。可以使用类定义自定义上下文管理器:

1
2
3
4
5
6
7
8
9
10
11
12
class ContextManager(object):

    def __init__(self, args):
        pass

    def __enter__(self):
        # Entrance logic here, called before entry of with block
        pass

    def __exit__(self, exception_type, exception_val, trace):
        # Exit logic here, called at exit of with block
        return True

然后入口被传递一个ContextManager类的实例,并且可以引用在__init__方法(文件、套接字等)中创建的任何内容。exit方法还接收在内部块和堆栈跟踪对象或None中引发的任何异常(如果逻辑完成而没有引发)。

然后我们可以这样使用它:

1
2
with ContextManager(myarg):
    # ... code here ...

这对于管理资源生命周期、释放文件描述符、管理异常以及构建嵌入式DSL等更复杂的用途都很有用。

另一种(但等效的)构造方法是contextlib装饰器,它使用一个生成器来分离出入口逻辑。

1
2
3
4
5
6
7
from contextlib import contextmanager

@contextmanager
def ContextManager(args):
    # Entrance logic here
    yield
    # Exit logic here


with看作是在代码块上创建一个"主管"(上下文管理器)。甚至可以给主管一个名称并在块中引用。当代码块正常结束或通过异常结束时,会通知主管,主管可以根据发生的情况采取适当的措施。