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)
以前と同じように機能します。
as
キーワードは、Python 3.8のwith
ステートメントでどのように使用されますか?これは、Zen of Pythonに対してではありません:「それを行うための明白な方法は1つ(できれば1つだけ)あるはずです。」?
この機能が最初に提案されたとき、代入式を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
ただし、括弧で囲まれた式は機能します。
TL; DR:2つの例の間に識別可能な違いはないとしても、動作は両方の構成要素で同じではありません。
with
ステートメントで_:=
_を必要とすることはほとんどないはずです。疑問がある場合は、with
ブロック内の管理対象オブジェクトが必要な場合は、常に_with ... as ...
_を使用してください。
_with context_manager as managed
_では、managed
はcontext_manager.__enter__()
の戻り値にバインドされますが、with (managed := context_manager)
ではmanaged
は_context_manager
_自体にバインドされており、__enter__()
メソッド呼び出しの戻り値はdiscardedです。 ___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)
_
つまり、with (f := open(...))
はf
をopen
の戻り値に設定しますが、with open(...) as f
はf
をimplicit__enter__()
メソッド呼び出し。
現在、ファイルとストリームの場合、file.__enter__()
は成功するとself
を返すので、これらの2つのアプローチはほとんど同じです-唯一の違いは、___enter__
_が例外をスローする場合です。
_mgr.__enter__()
がdistinctであるオブジェクトを返す多くのクラスがあるため、代入式がas
の代わりに機能することが多いという事実は誤解を招きます。 self
から。その場合、割り当て式の動作は異なります。管理対象オブジェクトではなく、コンテキストマネージャが割り当てられます。たとえば _unittest.mock.patch
_ は、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
_が、新しいモックオブジェクトではなく、コンテキストマネージャーにバインドされるようになりました。