funciones funcion python python-3.8 python-assignment-expression

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.