没有“as”关键字的“with”语句的含义

时间:2014-10-13 14:46:52

标签: python with-statement contextmanager

我熟悉使用python的with语句作为在抛出异常时确保对象最终化的方法。这通常看起来像

with file.open('myfile.txt') as f:
    do stuff...

这是

的简写
f = file.open('myfile.txt'):
try:
    do stuff...
finally:
    f.close()

或类可能出现的任何其他终结例程。

我最近遇到了一段处理OpenGL的代码:

with self.shader:
    (Many OpenGL commands)

请注意,没有任何as个关键字。这是否表明仍然要调用该类的__enter____exit__方法,但是该对象永远不会在块中显式使用(即,它通过全局或隐式引用来工作)?还是有一些其他含义让我不知所措?

2 个答案:

答案 0 :(得分:36)

上下文管理器可以可选返回一个对象,并将其分配给as指定的标识符。它是由__enter__分配的as方法返回的对象,不一定是上下文管理器本身。

在创建对象时使用as <identifier>有助于open()调用,但并非所有上下文管理器都是为上下文创建的。例如,它们可以重复使用并且已经创建。

进行数据库连接。您只创建一次数据库连接,但许多数据库适配器允许您将连接用作上下文管理器;输入上下文并启动一个事务,退出它并且事务被提交(成功)或回滚(当有异常时):

with db_connection:
    # do something to the database

此处不需要创建新对象,使用db_connection.__enter__()输入上下文,然后使用db_connection.__exit__()再次退出,但我们已经拥有对连接对象的引用。

现在,可以是您输入时连接对象生成游标对象。现在,将该游标对象分配为本地名称是有意义的:

with db_connection as cursor:
    # use cursor to make changes to the database

db_connection在这里仍然没有被调用,它之前已经存在,我们已经有了它的引用。但是,生成的db_connection.__enter__()现在已分配给cursor,并且可以在此处使用。

这是文件对象发生的事情; open()返回一个文件对象,fileobject.__enter__()返回文件对象本身,因此您可以在open()语句中使用with调用在一个步骤中分配对新创建的对象的引用,而不是两个。如果没有这个小技巧,你必须使用:

f = open('myfile.txt')
with f:
    # use `f` in the block

将所有这些应用到着色器示例中;您已经引用了self.shaderself.shader.__enter__()很可能会再次返回对self.shader的引用,但由于您已经拥有完全可用的引用,为什么要为此创建一个新的本地?

答案 1 :(得分:0)

上面的答案很好。

我在阅读时一直问自己的唯一一件事是,以下场景的确认在哪里。如果 with 语句的上下文主体中有赋值,则赋值右侧的任何内容首先“绑定”到上下文。因此,在以下内容中:

with db_connection():
   result = select(...)

... select 是 ~ ref_to_connection.select(...)

我把它放在这里是给像我这样在语言之间来来回回的人,如果能快速提醒一下如何阅读和跟踪这里的引用,可能会从中受益。