对于Python3.8中的赋值表达式,为什么需要在With中使用“as”?

2024-04-18 14:35:53 发布

您现在位置:Python中文网/ 问答频道 /正文


Tags: python
1条回答
网友
1楼 · 发布于 2024-04-18 14:35:53

TL;DR:这两个构造的行为并不相同,尽管这两个例子之间没有明显的区别。在

您几乎不应该在with语句中使用:=,有时这是非常错误的。如果有疑问,当您需要with块中的托管对象时,请始终使用with ... as ...。在


with context_manager as managed中,managed被绑定到context_manager.__enter__()返回值,而在with (managed := context_manager)中,managed绑定到context_manager本身,__enter__()方法调用的返回值是丢弃的。对于打开的文件,这种行为几乎是相同的,因为它们的__enter__方法返回self。在

第一个节选是roughly analogous to

_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形式是

^{pr2}$

with (f := open(...))f设置为open的返回值,而with open(...) as f将{}绑定到隐式__enter__()方法调用的返回值。在

现在,对于文件和流,如果成功,file.__enter__()将返回{},因此这两种方法的行为几乎是相同的-唯一的区别是__enter__抛出异常。在

赋值表达式通常可以代替as起作用,这一事实是有欺骗性的,因为在许多类中,_mgr.__enter__()返回的对象与self不同。在这种情况下,赋值表达式的工作方式不同:分配上下文管理器,而不是托管对象。例如,^{}是一个上下文管理器,它将返回mock对象。它的文档有以下示例:

>>> 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现在绑定到上下文管理器,而不是新的模拟对象。在

相关问题 更多 >