18

I saw this question, and I understand when you would want to use with foo() as bar:, but I don't understand when you would just want to do:

bar = foo()
with bar:
   ....

Doesn't that just remove the tear-down benefits of using with ... as, or am I misunderstanding what is happening? Why would someone want to use just with?

4

3 回答 3

12

为了扩展@freakish的答案,with保证进入然后退出“上下文”。上下文到底是什么?嗯,它是“不管你用什么东西做的”。一些明显的是:

  • 锁:你拿一个锁,操作一些数据,然后释放锁。
  • 外部文件/流:您打开一个文件,读取或写入它,然后关闭它。
  • 数据库记录:您找到一条记录(通常也会锁定它),使用一些字段并可能更改它们,然后释放记录(这也会解锁它)。

不太明显的甚至可能包括某些类型的异常捕获:您可能会捕获除以零,进行一些算术运算,然后停止捕获它。当然,这是内置在 Python 语法中的:try……except作为一个块!而且,实际上,with它只是 Python 的 try/except/finally 机制的一个特例(从技术上讲,是try/finally包裹在另一个try块上;参见注释)。

当上下文条目提供一些您想在块内使用的值时,块的as一部分很有用。with对于文件或数据库记录,很明显您需要新打开的流或刚刚获得的记录。在捕获异常或持有数据结构锁的情况下,可能不需要从上下文条目中获取值。

于 2013-06-11T20:44:00.070 回答
5

例如,当您想使用Lock()

from threading import Lock
myLock = Lock()
with myLock:
   ...

你真的不需要这个Lock()对象。您只需要知道它已开启。

于 2013-06-11T20:26:40.700 回答
4

使用withwithoutas仍然可以获得完全相同的拆解;它只是不会为您提供表示上下文的新本地对象。

您想要这样做的原因是,有时上下文本身并不直接有用——换句话说,您只是将它用于上下文进入和退出的副作用。

例如,对于一个Lock对象,您必须已经拥有该with块才能使用的对象——因此,即使您在块中需要它,也没有理由将其重新绑定到另一个名称。当您contextlib.closing在不是上下文管理器的对象上使用时也是如此 - 您已经拥有对象本身,所以谁在乎closing产生什么?

使用类似的东西sh.sudo,甚至没有一个你可以使用的对象,句号。

在某些情况下,上下文管理器的目的只是为了存储和自动恢复某些状态。例如,您可能想编写一个termios.tcsetattr-stasher,以便您可以tty.setraw()在块内调用。您不在乎存储对象的外观,您只关心它会自动恢复。

decimal.localcontext可以以任何这些方式工作——你可以传递一个你已经拥有的对象(因此不需要一个新名称),或者传递一个未命名的临时对象,或者让它只是将当前上下文存储为自动恢复。但在任何这些情况下。

在某些混合情况下,有时您需要上下文,有时则不需要。例如,如果您只想让数据库事务自动提交,您可以编写with autocommit(db.begin()):,因为您不会在块内访问它。但是如果你想让它自动回滚,除非你明确地提交它,你可能会写with autorollback(db.begin()) as trans:,所以你可以trans.commit()在块内。(当然,通常情况下,您实际上想要一个在正常退出时提交并在异常时回滚的事务,如PEP 343transaction示例。但我想不出一个更好的混合示例......)

PEP 343及其前身(PEP 310、PEP 340 以及与 343 相关联的其他内容)在一定程度上解释了所有这些,但可以理解的是,您不会在随便阅读时就明白这一点——有太多信息不是t 相关的,它主要只是解释了英里高的概述,然后是实现级别的细节,跳过了介于两者之间的所有内容。

于 2013-06-11T20:43:52.390 回答