上下文管理器可以选择性地返回一个对象,该对象将被分配给由as
命名的标识符。 被分配给as
的是__enter__
方法返回的对象,而不一定是上下文管理器本身。
使用as <identifier>
有助于创建像open()
调用这样的新对象,但并非所有的上下文管理器都是为上下文而创建的。 它们可以重复使用,已经被创建,例如数据库连接。
以数据库连接为例。您只需创建一次数据库连接,但许多数据库适配器允许您将连接用作上下文管理器; 进入上下文时会启动事务,退出时便会提交(成功)或回滚(当有异常时):
with db_connection:
这里不需要创建新的对象,在使用db_connection.__enter__()
进入上下文,然后再使用db_connection.__exit__()
退出上下文,但我们已经有连接对象的引用。
现在,如果连接对象在进入时生成游标对象,则将该游标对象分配到本地名称中是有意义的:
with db_connection as cursor:
db_connection
在这里还没有被调用,它在此之前已经存在,我们已经有它的引用。但是无论 db_connection.__enter__()
产生了什么,现在都被赋值给了 cursor
,并可以从那里继续使用。
文件对象也是这样处理的;open()
返回一个文件对象,fileobject.__enter__()
返回文件对象本身,所以你可以在一个步骤中使用 with
语句调用 open()
并分配一个新创建对象的引用,而不是两个步骤。如果没有那个小技巧,你将需要使用:
f = open('myfile.txt')
with f:
将所有这些应用于您的着色器示例; 您已经引用了self.shader
。 很可能self.shader.__enter__()
返回对self.shader
的引用,但是既然您已经拥有完全可用的引用,为什么要为此创建新的本地引用?
with
块内部别名上下文管理器,那么没关系 - 例如contextlib.suppress
。严格来说,您可以执行with open(...): ...
,但是由于您无法访问文件处理程序,因此没有太多意义! - jonrsharpe