Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

630
Views
Invocar un constructor en una declaración 'with'

Tengo el siguiente código:

 class Test: def __init__(self, name): self.name = name def __enter__(self): print(f'entering {self.name}') def __exit__(self, exctype, excinst, exctb) -> bool: print(f'exiting {self.name}') return True with Test('first') as test: print(f'in {test.name}') test = Test('second') with test: print(f'in {test.name}')

Ejecutarlo produce el siguiente resultado:

 entering first exiting first entering second in second exiting second

Pero esperaba que produjera:

 entering first in first exiting first entering second in second exiting second

¿Por qué no se llama el código dentro de mi primer ejemplo?

over 4 years ago · Hanz Gallego
5 answers
Answer question

0

El método __enter__ debería devolver el objeto de contexto. with ... as ... usa el valor de retorno de __enter__ para determinar qué objeto darle. Dado que su __enter__ no devuelve nada, implícitamente devuelve None , por lo que test es None .

 with Test('first') as test: print(f'in {test.name}') test = Test('second') with test: print(f'in {test.name}')

Así que test es ninguna. Entonces test.name es un error. Ese error se genera, por lo que se llama a Test('first').__exit__ . __exit__ devuelve True , lo que indica que el error se manejó (esencialmente, que su __exit__ está actuando como un bloque de except ), por lo que el código continúa después del primer bloque with , ya que le dijo a Python que todo estaba bien.

Considerar

 def __enter__(self): print(f'entering {self.name}') return self

También puede considerar no devolver True de __exit__ a menos que realmente tenga la intención de suprimir incondicionalmente todos los errores en el bloque (y comprenda completamente las consecuencias de suprimir los errores de otros programadores, así como KeyboardInterrupt , StopIteration y varias señales del sistema)

over 4 years ago · Hanz Gallego Report

0

Creo que este comportamiento se debe a que __enter__ debe devolver algo sobre lo que se operará, que en este caso se accederá con el nombre test . Cambiando __enter__ a lo siguiente

 def __enter__(self): print(f"entering {self.name}") return self

obtenemos el comportamiento esperado.

over 4 years ago · Hanz Gallego Report

0

El problema es que su método __enter__ devuelve None . Por lo tanto, a test se le asigna None .

Luego intenta acceder a (None).name , lo que genera un error. Dado que su método __exit__ siempre devuelve True , suprimirá cualquier error. Según los documentos :

Devolver un valor verdadero de este método hará que la declaración with suprima la excepción y continúe la ejecución con la declaración que sigue inmediatamente a la declaración with.

over 4 years ago · Hanz Gallego Report

0

Explicación:

__enter__ está dando None como salida, ya que no hay return , por lo tanto, activaría directamente __exit__ ya que None no tiene un name de atributo, por ejemplo:

 >>> None.name Traceback (most recent call last): File "<pyshell#0>", line 1, in <module> None.__name__ AttributeError: 'NoneType' object has no attribute 'name' >>>

Si lo configura para llamar a __class__.__name__ ( None de los objetos tiene ese atributo, lo que da NoneType ), podría encontrar el problema fácilmente:

 class Test: def __init__(self, name): self.name = name def __enter__(self): print(f'entering {self.name}') def __exit__(self, exctype, excinst, exctb) -> bool: print(f'exiting {self.name}') return True with Test('first') as test: print(f'in {test.__class__.__name__}') test = Test('second') with test: print(f'in {test.__class__.__name__}')

Producción:

 entering first in NoneType exiting first entering second in Test exiting second

Como puede ver, dice in NoneType , no devolver ningún valor es la razón de esto. En muchos casos, __enter__ no necesita regresar, pero en este caso, la clase Test necesita que regrese.

Solución:

La solución sería mantener la instancia de Test , de modo que llame al name de un self devuelto después del resultado __enter__ del administrador de contexto. Hasta ahora, __enter__ da como resultado None , por lo tanto, el atributo None.name no existe. Entonces, si devuelve self , existiría el atributo test.name .

La solución sería devolverse a self en la implementación del método mágico __enter__ :

 ... def __enter__(self): print(f'entering {self.name}') return self ...

Código completo:

 class Test: def __init__(self, name): self.name = name def __enter__(self): print(f'entering {self.name}') return self def __exit__(self, exctype, excinst, exctb) -> bool: print(f'exiting {self.name}') return True with Test('first') as test: print(f'in {test.name}') test = Test('second') with test: print(f'in {test.name}')

Producción:

 entering first in first exiting first entering second in second exiting second

La información adicional que di que las otras respuestas no dieron es una prueba más concreta de la implementación del método __enter__ que da None . También mostré un ejemplo de ello.

over 4 years ago · Hanz Gallego Report

0

La razón es que el primer y segundo caso no hacen lo mismo.

en first :

  • se crea el objeto, que llama a __init__ ;
  • luego with llamadas __enter__ ;
  • luego as almacena el resultado de __enter__ en la test
  • dado que __enter__ no tiene un valor de retorno, test es None .

en second :

  • se crea el objeto, que llama a __init__ ;
  • luego asignado a la test ;
  • luego with llamadas __enter__ ;
  • pero no se hace nada con el resultado de __enter__ ;
  • entonces la test sigue refiriéndose al objeto que se creó originalmente.

En ambos casos, se llama a __exit__ para el objeto with se está manejando, por lo que verá que se imprime la etiqueta correcta; es solo que, en first , el identificador de test no está vinculado a ese mismo objeto.

NB __enter__ no tiene que devolver self . Puede devolver algo completamente diferente, por ejemplo, puede abrir un archivo y hacer que __enter__ devuelva la secuencia, mientras que __exit__ puede cerrarlo. Si se diera por sentado que __enter__ debería devolver self , eso sería redundante y simplemente podría estar implícito.

over 4 years ago · Hanz Gallego Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!