现在PEP 572已被接受,Python 3.8注定要具有赋值表达式,因此我们可以在with
中使用赋值表达式,即
with (f := open('file.txt')):
for l in f:
print(f)
代替
with open('file.txt') as f:
for l in f:
print(f)
,它将像以前一样工作。
在Python 3.8中,as
关键字与with
语句有什么用?这不是违背Python的禅宗吗:“应该有一种-最好只有一种-显而易见的方法。”
最初提出该功能时,并未明确指定是否应在with
中加上括号并赋予
with f := open('file.txt'):
for l in f:
print(f)
可以工作。但是,在Python 3.8a0中,
with f := open('file.txt'):
for l in f:
print(f)
将导致
File "<stdin>", line 1
with f := open('file.txt'):
^
SyntaxError: invalid syntax
但带括号的表达式有效。
答案 0 :(得分:32)
TL; DR :即使两个示例之间没有明显的差异,两种构造的行为也不相同。
您几乎永远不需要在:=
语句中使用with
,有时这是非常错误的。如有疑问,请在with ... as ...
块中需要托管对象时始终使用with
。
在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
格式为
_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)
即with (f := open(...))
会将f
设置为open
的返回值,而with open(...) as f
将f
绑定到隐式 { {1}}方法调用。
现在,如果是文件和流,__enter__()
如果成功将返回file.__enter__()
,所以这两种方法的行为几乎是 相同-唯一的区别在于self
引发异常。
赋值表达式通常可以代替__enter__
起作用的事实具有欺骗性,因为as
在许多类中返回的对象与{{1} }。在这种情况下,赋值表达式的工作方式有所不同:分配了上下文管理器,而不是托管对象。例如,unittest.mock.patch
是一个上下文管理器,它将返回 mock 对象。其文档具有以下示例:
_mgr.__enter__()
现在,如果将其编写为使用赋值表达式,则行为将有所不同:
self
>>> 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
现在绑定到上下文管理器,而不是新的模拟对象。