在 with 语句中或之前评估和分配表达式

Evaluate and assign expression in or before with statement

如果我是正确的,with 语句不会为 with 语句引入局部作用域。

这些是学习中的例子 Python:

with open(r'C:\misc\data') as myfile:
    for line in myfile:
        print(line)
        ...more code here...

lock = threading.Lock()                        # After: import threading
with lock:
    # critical section of code
    ...access shared resources...

第二个例子是否等同于下面以类似于第一个例子的方式重写?

with threading.Lock() as lock:
    # critical section of code
    ...access shared resources...

它们有什么区别?

第一个例子是否等同于下面以类似于第二个例子的方式重写?

myfile = open(r'C:\misc\data')
with myfile:
    for line in myfile:
        print(line)
        ...more code here...

它们有什么区别?

Here's a good explanation。我将解释关键部分:

The with statement could be thought of like this code:

set things up
try:
    do something
finally:
    tear things down

Here, “set things up” could be opening a file, or acquiring some sort of external resource, and “tear things down” would then be closing the file, or releasing or removing the resource. The try-finally construct guarantees that the “tear things down” part is always executed, even if the code that does the work doesn’t finish.

with 进入上下文时,它会调用上下文管理器对象上的挂钩,称为 __enter__,并且可以选择将该挂钩的 return 值分配给一个名称使用 as <name>。许多上下文管理器 return self 来自他们的 __enter__ 钩子。如果他们这样做,那么您确实可以在单独的行上创建上下文管理器或使用 as.

捕获对象之间做出选择。

在您的两个示例中,只有从 open() 编辑的文件对象 return 具有 return 与 self__enter__ 挂钩。对于 threading.Lock()__enter__ return 与 Lock.acquire() 相同的值,因此 布尔值 ,而不是锁定对象本身。

您需要查找明确的文档来证实这一点;然而,这并不总是那么清楚。对于 Lock 个对象,relevant section of the documentation 状态:

All of the objects provided by this module that have acquire() and release() methods can be used as context managers for a with statement. The acquire() method will be called when the block is entered, and release() will be called when the block is exited.

对于文件对象,IOBase documentation 相当模糊,您必须从示例中推断文件对象是 returned.

要记住的主要事情是 returning self 不是强制性的 ,也不是总是需要的。上下文管理器完全可以 return 其他东西。例如,许多数据库连接对象是上下文管理器,可让您管理事务(自动回滚或提交,具体取决于是否存在异常),其中输入 returns 一个绑定到连接的新游标对象。

明确地说:

  • 对于您的 open() 示例,这两个示例的所有意图和目的完全相同。两者都调用 open(),如果没有引发异常,您最终会引用名为 myfile 的文件对象。在这两种情况下,文件对象都将在 with 语句完成后关闭。该名称在 with 语句完成后继续存在。

    有区别,但主要是技术上的。对于 with open(...) as myfile:,创建文件对象,调用它的 __enter__ 方法,然后绑定 myfile。对于myfile = open(...)情况,myfile先绑定,__enter__后调用。

  • 对于您的 with threading.Lock() as lock: 示例,使用 as lock 会将 lock 设置为 True(锁定总是成功或以这种方式无限期阻塞) .这不同于 lock = threading.Lock() 情况,其中 lock 绑定到锁对象。