TL;DR:这两种构造的行为并不相同,即使这两个示例之间没有明显的差异。
你应该几乎从不需要:=
在一个with
声明中,有时它是非常错误的。如有疑问,请始终with ... as ...
在您需要块内的托管对象时使用with
。
in with context_manager as managed
,managed
绑定到 的返回值,context_manager.__enter__()
而 in with (managed := context_manager)
,managed
绑定到context_manager
自身,并且__enter__()
方法调用的返回值被丢弃。打开文件的行为几乎相同,因为它们的__enter__
方法返回self
.
第一个摘录大致类似于
_mgr = (f := open('file.txt')) # `f` is assigned here, even if `__enter__` fails
_mgr.__enter__() # the return value is discarded
exc = True
try:
try:
BLOCK
except:
# The exceptional case is handled here
exc = False
if not _mgr.__exit__(*sys.exc_info()):
raise
# The exception is swallowed if exit() returns true
finally:
# The normal and non-local-goto cases are handled here
if exc:
_mgr.__exit__(None, None, None)
而as
形式是
_mgr = open('file.txt') #
_value = _mgr.__enter__() # the return value is kept
exc = True
try:
try:
f = _value # here f is bound to the return value of __enter__
# and therefore only when __enter__ succeeded
BLOCK
except:
# The exceptional case is handled here
exc = False
if not _mgr.__exit__(*sys.exc_info()):
raise
# The exception is swallowed if exit() returns true
finally:
# The normal and non-local-goto cases are handled here
if exc:
_mgr.__exit__(None, None, None)
iewith (f := open(...))
将设置f
为的返回值open
,而with open(...) as f
绑定f
到隐式 __enter__()
方法调用的返回值。
现在,在文件和流的情况下,如果成功file.__enter__()
将返回,因此这两种方法的行为几乎相同 - 唯一的区别在于引发异常的事件。self
__enter__
赋值表达式通常会起作用的事实具有欺骗性as
,因为有许多类_mgr.__enter__()
返回的对象与. 在这种情况下,赋值表达式的工作方式不同:分配上下文管理器,而不是托管对象。例如,一个将返回模拟对象的上下文管理器。它的文档有以下示例:self
unittest.mock.patch
>>> thing = object()
>>> with patch('__main__.thing', new_callable=NonCallableMock) as mock_thing:
... assert thing is mock_thing
... thing()
...
Traceback (most recent call last):
...
TypeError: 'NonCallableMock' object is not callable
现在,如果要编写它以使用赋值表达式,则行为会有所不同:
>>> thing = object()
>>> with (mock_thing := patch('__main__.thing', new_callable=NonCallableMock)):
... assert thing is mock_thing
... thing()
...
Traceback (most recent call last):
...
AssertionError
>>> thing
<object object at 0x7f4aeb1ab1a0>
>>> mock_thing
<unittest.mock._patch object at 0x7f4ae910eeb8>
mock_thing
现在绑定到上下文管理器而不是新的模拟对象。