funcion - Con las expresiones de asignación en Python 3.8, ¿por qué necesitamos usar `as` en` con`?
funcion lambda python (1)
TL; DR : El comportamiento no es el mismo para ambas construcciones, aunque no habría diferencias discernibles entre los 2 ejemplos.
Casi nunca debería necesitar :=
en una sentencia with
, y algunas veces es muy incorrecto. En caso de duda, utilice siempre with ... as ...
cuando necesite el objeto gestionado dentro del bloque with
.
En with context_manager as managed
, managed
está vinculado al valor de retorno de context_manager.__enter__()
, mientras que en with (managed := context_manager)
, managed
está enlazado al context_manager
y el valor de retorno de la llamada al método __enter__()
se descarta . El comportamiento es casi idéntico para los archivos abiertos, porque su método __enter__
devuelve self
.
El primer extracto es aproximadamente análogo a
_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)
mientras que la forma as
sería
_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)
es decir, with (f := open(...))
establecería f
al valor de retorno de open
, mientras que with open(...) as f
vincula f
al valor de retorno de la llamada de método __enter__()
implícita .
Ahora, en el caso de archivos y secuencias , el file.__enter__()
se devolverá solo si tiene éxito, por lo que el comportamiento de estos dos enfoques es casi el mismo: la única diferencia es en el caso de que __enter__
arroje una excepción.
El hecho de que las expresiones de asignación funcionen a menudo en lugar de as
es engañoso, porque hay muchas clases en las que _mgr.__enter__()
devuelve un objeto que es distinto de self
. En ese caso, una expresión de asignación funciona de manera diferente: se asigna el administrador de contexto, en lugar del objeto administrado . Por ejemplo, unittest.mock.patch
es un administrador de contexto que devolverá el objeto simulado . La documentación para ello tiene el siguiente ejemplo:
>>> 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
Ahora, si se escribiera para usar una expresión de asignación, el comportamiento sería diferente:
>>> 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
ahora está vinculado al administrador de contexto en lugar del nuevo objeto simulado.
Ahora que se ha aceptado PEP 572 , Python 3.8 está destinado a tener expresiones de asignación , por lo que podemos usar una expresión de asignación with
, es decir,
with (f := open(''file.txt'')):
for l in f:
print(f)
en lugar de
with open(''file.txt'') as f:
for l in f:
print(f)
Y funcionaría como antes.
¿Qué uso tiene la palabra clave as
con la declaración with
en Python 3.8? ¿No es esto contra el Zen de Python: "Debería haber una, y preferiblemente una, manera obvia de hacerlo"? ?
Cuando se propuso originalmente la característica, no se especificó claramente si la expresión de la asignación debería estar entre paréntesis y with
with f := open(''file.txt''):
for l in f:
print(f)
podría funcionar. Sin embargo, en Python 3.8a0,
with f := open(''file.txt''):
for l in f:
print(f)
causará
File "<stdin>", line 1
with f := open(''file.txt''):
^
SyntaxError: invalid syntax
pero la expresión entre paréntesis funciona.