Python - afirmar vs si y regresar

12

Estoy escribiendo un script que hace algo en un archivo de texto (sin embargo, lo que hace es irrelevante para mi pregunta). Entonces, antes de hacer algo al archivo, quiero verificar si el archivo existe. Puedo hacer esto, no hay problema, pero el problema es más estético.

Aquí está mi código, implementando lo mismo de dos maneras diferentes.

def modify_file(filename):
    assert os.path.isfile(filename), 'file does NOT exist.'


Traceback (most recent call last):
  File "clean_files.py", line 15, in <module>
    print(clean_file('tes3t.txt'))
  File "clean_files.py", line 8, in clean_file
    assert os.path.isfile(filename), 'file does NOT exist.'
AssertionError: file does NOT exist.

o:

def modify_file(filename):
    if not os.path.isfile(filename):
        return 'file does NOT exist.'


file does NOT exist.

El primer método produce una salida que es en su mayoría trivial, lo único que me importa es que el archivo no existe.

El segundo método devuelve una cadena, es simple.

Mi pregunta es: ¿qué método es mejor para que el usuario sepa que el archivo no existe? Usar el assertmétodo parece de alguna manera más pitónico.

Vader
fuente

Respuestas:

33

En su lugar, optaría por una tercera opción: uso raisey una excepción específica. Esta puede ser una de las excepciones integradas , o puede crear una excepción personalizada para el trabajo.

En este caso, usaría IOError, pero ValueErrortambién podría encajar:

def modify_file(filename):
    if not os.path.isfile(filename):
        raise IOError('file does NOT exist.')

El uso de una excepción específica le permite generar otras excepciones para diferentes circunstancias excepcionales y la persona que llama maneja la excepción con gracia.

Por supuesto, muchas operaciones de archivo (como open()) ellos mismos plantean OSErrorya; explícitamente primero probar si el archivo existe puede ser redundante aquí.

No utilizar assert; Si ejecuta Python con la -Obandera, todas las aserciones se eliminan del código.

Martijn Pieters
fuente
punto interesante sobre la redundancia! ¿Recomendarías evitarlo? es decir, "fallará de todos modos más adelante"
Ciprian Tomoiagă
1
@ CiprianTomoiagă: ¿por qué duplicar en las pruebas? Si la siguiente línea de modify_file()es with open(filename) as f:, entonces IOErrortambién se planteará. Y las versiones más recientes de Python han proporcionado más detalles en las subclases de IOError( FileNotFoundErrorespecíficamente viene a la mente) que podrían ser útiles para un desarrollador que utiliza esta API. Si el código realiza sus propios controles y aumentos IOError, se perderían esos detalles útiles.
Martijn Pieters
@MartijnPieters sería una respuesta aceptable a "¿por qué duplicar en las pruebas?" ¿Cuándo cuando la primera verificación es más rápida que la excepción generada cuando open () falla? por ejemplo, cuando verificar la existencia de un archivo es más rápido que intentar abrirlo y finalmente no hacerlo.
Marcel Wilson el
1
@MarcelWilson: No, porque estarías micro optimizando contra un método que hace E / S. Ninguna cantidad de ajustes en la semántica de Python hará que la E / S vaya más rápido, y solo perjudicará la legibilidad y el mantenimiento. Enfoque en áreas con más impacto, diría.
Martijn Pieters
12

assertestá destinado a casos en los que el programador que llamó a la función cometió un error, a diferencia del usuario . El uso assertbajo esa circunstancia le permite asegurarse de que un programador esté usando su función correctamente durante la prueba, pero luego lo elimina en producción.

Su valor es algo limitado, ya que debe asegurarse de ejercer esa ruta a través del código, y a menudo desea manejar adicionalmente el problema con una ifdeclaración separada en producción. assertes más útil en situaciones como, "Quiero solucionar este problema de manera útil si un usuario lo golpea, pero si un desarrollador lo golpea, quiero que se bloquee con fuerza para que repare el código que llama incorrectamente a esta función".

En su caso particular, un archivo faltante es casi seguro un error del usuario, y debe manejarse con una excepción.

Karl Bielefeldt
fuente
5

De UsingAssertionsEffectively

La comprobación de isinstance () no se debe utilizar en exceso: si grazna como un pato, quizás no haya necesidad de investigar demasiado si realmente es así. A veces puede ser útil pasar valores que no fueron anticipados por el programador original.

Lugares para considerar poner afirmaciones:

checking parameter types, classes, or values
checking data structure invariants
checking "can't happen" situations (duplicates in a list, contradictory state variables.)
after calling a function, to make sure that its return is reasonable 

El punto general es que si algo sale mal, queremos que sea completamente obvio lo antes posible.

Es más fácil capturar datos incorrectos en el punto donde ingresan que averiguar cómo llegaron allí más tarde cuando causan problemas.

Las afirmaciones no son un sustituto de las pruebas unitarias o las pruebas del sistema, sino más bien un complemento. Debido a que las afirmaciones son una forma clara de examinar el estado interno de un objeto o función, proporcionan "gratis" una asistencia de caja clara a una prueba de caja negra que examina el comportamiento externo.

Las afirmaciones no deben usarse para probar los casos de falla que pueden ocurrir debido a una entrada incorrecta del usuario o fallas del sistema operativo / entorno, como un archivo que no se encuentra. En su lugar, debe generar una excepción, o imprimir un mensaje de error, o lo que sea apropiado. Una razón importante por la cual las aserciones solo deben usarse para las autocomprobaciones del programa es que las aserciones se pueden deshabilitar en tiempo de compilación.

Si Python se inicia con la opción -O, las aserciones se eliminarán y no se evaluarán. Entonces, si el código usa mucho las aserciones, pero es crítico para el rendimiento, entonces hay un sistema para desactivarlas en las versiones de lanzamiento. (Pero no haga esto a menos que sea realmente necesario. Se ha demostrado científicamente que algunos errores solo aparecen cuando un cliente usa la máquina y queremos que las aserciones también ayuden allí. :-))

dspjm
fuente