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.