Introducción
Aunque las excepciones se introdujeron anteriormente, esta sección completa algunos detalles adicionales sobre la comprobación de errores y el manejo de excepciones.
This tutorial is from open-source community. Access the source code
Aunque las excepciones se introdujeron anteriormente, esta sección completa algunos detalles adicionales sobre la comprobación de errores y el manejo de excepciones.
Python no realiza ninguna comprobación o validación de los tipos o valores de los argumentos de las funciones. Una función funcionará con cualquier dato que sea compatible con las instrucciones de la función.
def add(x, y):
return x + y
add(3, 4) ## 7
add('Hello', 'World') ## 'HelloWorld'
add('3', '4') ## '34'
Si hay errores en una función, aparecen en tiempo de ejecución (como una excepción).
def add(x, y):
return x + y
>>> add(3, '4')
Traceback (most recent call last):
...
TypeError: unsupported operand type(s) for +:
'int' and 'str'
>>>
Para verificar el código, se da una gran importancia a las pruebas (que se abordan más adelante).
Las excepciones se utilizan para señalar errores. Para generar una excepción por tu cuenta, utiliza la instrucción raise.
if name not in authorized:
raise RuntimeError(f'{name} not authorized')
Para capturar una excepción, utiliza try-except.
try:
authenticate(username)
except RuntimeError as e:
print(e)
Las excepciones se propagan hasta el primer except que coincide.
def grok():
...
raise RuntimeError('Whoa!') ## Se lanza la excepción aquí
def spam():
grok() ## Llamada que causará la excepción
def bar():
try:
spam()
except RuntimeError as e: ## Se captura la excepción aquí
...
def foo():
try:
bar()
except RuntimeError as e: ## La excepción no llega aquí
...
foo()
Para manejar la excepción, coloca las instrucciones en el bloque except. Puedes agregar cualquier instrucción que desees para manejar el error.
def grok():...
raise RuntimeError('Whoa!')
def bar():
try:
grok()
except RuntimeError as e: ## Se captura la excepción aquí
instrucciones ## Utiliza estas instrucciones
instrucciones
...
bar()
Después de manejar la excepción, la ejecución continúa con la primera instrucción después del try-except.
def grok():...
raise RuntimeError('Whoa!')
def bar():
try:
grok()
except RuntimeError as e: ## Se captura la excepción aquí
instrucciones
instrucciones
...
instrucciones ## Continúa la ejecución aquí
instrucciones ## Y sigue aquí
...
bar()
Hay aproximadamente dos docenas de excepciones integradas. Por lo general, el nombre de la excepción indica lo que está mal (por ejemplo, se lanza una ValueError porque se proporcionó un valor incorrecto). Esta no es una lista exhaustiva. Consulte la documentación para obtener más información.
ArithmeticError
AssertionError
EnvironmentError
EOFError
ImportError
IndexError
KeyboardInterrupt
KeyError
MemoryError
NameError
ReferenceError
RuntimeError
SyntaxError
SystemError
TypeError
ValueError
Las excepciones tienen un valor asociado. Contiene información más específica sobre lo que está mal.
raise RuntimeError('Invalid user name')
Este valor es parte de la instancia de excepción que se coloca en la variable suministrada a except.
try:
...
except RuntimeError as e: ## `e` contiene la excepción generada
...
e es una instancia del tipo de excepción. Sin embargo, a menudo parece una cadena cuando se imprime.
except RuntimeError as e:
print('Failed : Reason', e)
Puedes capturar diferentes tipos de excepciones utilizando múltiples bloques except.
try:
...
except LookupError as e:
...
except RuntimeError as e:
...
except IOError as e:
...
except KeyboardInterrupt as e:
...
Alternativamente, si las instrucciones para manejarlas son las mismas, puedes agruparlas:
try:
...
except (IOError,LookupError,RuntimeError) as e:
...
Para capturar cualquier excepción, use Exception de la siguiente manera:
try:
...
except Exception: ## PELIGRO. Ver abajo
print('An error occurred')
En general, escribir código así es una mala idea porque no tendrás idea de por qué falló.
A continuación se muestra la manera incorrecta de utilizar excepciones.
try:
go_do_something()
except Exception:
print('Computer says no')
Esto captura todos los posibles errores y puede hacer imposible el depuración cuando el código falla por alguna razón que no esperabas en absoluto (por ejemplo, un módulo de Python desinstalado, etc.).
Si vas a capturar todos los errores, este es un enfoque más razonable.
try:
go_do_something()
except Exception as e:
print('Computer says no. Reason :', e)
Reporta una razón específica de falla. Casi siempre es una buena idea tener algún mecanismo para ver/reportar errores cuando escribes código que captura todas las posibles excepciones.
En general, sin embargo, es mejor capturar el error tan específicamente como sea razonable. Solo captura los errores que puedas realmente manejar. Deja que otros errores pasen - quizás algún otro código los pueda manejar.
Utiliza raise para propagar un error capturado.
try:
go_do_something()
except Exception as e:
print('Computer says no. Reason :', e)
raise
Esto te permite tomar medidas (por ejemplo, registrar) y pasar el error al llamador.
No captures excepciones. Fallo rápido y con ruido. Si es importante, alguien más se ocupará del problema. Solo captura una excepción si eres ese alguien. Es decir, solo captura errores donde puedas recuperarte y seguir adelante de manera razonable.
finallyEspecifica código que debe ejecutarse independientemente de si se produce una excepción o no.
lock = Lock()
...
lock.acquire()
try:
...
finally:
lock.release() ## esto SIEMPRE se ejecutará. Con o sin excepción.
Comúnmente se utiliza para administrar de manera segura los recursos (especialmente los candados, archivos, etc.).
withEn el código moderno, try-finally a menudo se reemplaza con la sentencia with.
lock = Lock()
with lock:
## lock adquirido
...
## lock liberado
Un ejemplo más familiar:
with open(filename) as f:
## Utilizar el archivo
...
## Archivo cerrado
with define un contexto de uso para un recurso. Cuando la ejecución sale de ese contexto, los recursos se liberan. with solo funciona con ciertos objetos que han sido programados específicamente para soportarlo.
La función parse_csv() que escribiste en la última sección permite la selección de columnas especificadas por el usuario, pero eso solo funciona si el archivo de datos de entrada tiene encabezados de columna.
Modifica el código para que se lance una excepción si se pasan ambos argumentos select y has_headers=False. Por ejemplo:
>>> parse_csv('/home/labex/project/prices.csv', select=['name','price'], has_headers=False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 9, in parse_csv
raise RuntimeError("select argument requires column headers")
RuntimeError: select argument requires column headers
>>>
Luego de agregar esta comprobación, podrías preguntarte si deberías realizar otros tipos de comprobaciones de validez en la función. Por ejemplo, ¿deberías comprobar que el nombre de archivo sea una cadena, que tipos sea una lista o algo por el estilo?
Como regla general, por lo general es mejor omitir esas pruebas y simplemente dejar que el programa falle con entradas incorrectas. El mensaje de traza apuntará a la fuente del problema y puede ayudar en la depuración.
La principal razón para agregar la comprobación anterior es evitar ejecutar el código en un modo sin sentido (por ejemplo, usar una característica que requiere encabezados de columna, pero al mismo tiempo especificar que no hay encabezados).
Esto indica un error de programación en el código llamador. Comprobar casos que "no deben suceder" a menudo es una buena idea.
La función parse_csv() que escribiste se utiliza para procesar todo el contenido de un archivo. Sin embargo, en el mundo real, es posible que los archivos de entrada tengan datos dañados, faltantes o sucios. Intenta este experimento:
>>> portfolio = parse_csv('missing.csv', types=[str, int, float])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "fileparse.py", line 36, in parse_csv
row = [func(val) for func, val in zip(types, row)]
ValueError: invalid literal for int() with base 10: ''
>>>
Modifica la función parse_csv() para capturar todas las excepciones ValueError generadas durante la creación de registros y mostrar un mensaje de advertencia para las filas que no se pueden convertir.
El mensaje debe incluir el número de fila y la información sobre el motivo por el cual falló. Para probar tu función, intenta leer el archivo missing.csv de arriba. Por ejemplo:
>>> portfolio = parse_csv('missing.csv', types=[str, int, float])
Fila 4: No se pudo convertir ['MSFT', '', '51.23']
Fila 4: Razón literal no válida para int() con base 10: ''
Fila 7: No se pudo convertir ['IBM', '', '70.44']
Fila 7: Razón literal no válida para int() con base 10: ''
>>>
>>> portfolio
[{'name': 'AA','shares': 100, 'price': 32.2}, {'name': 'IBM','shares': 50, 'price': 91.1}, {'name': 'CAT','shares': 150, 'price': 83.44}, {'name': 'GE','shares': 95, 'price': 40.37}, {'name': 'MSFT','shares': 50, 'price': 65.1}]
>>>
Modifica la función parse_csv() para que los mensajes de error de análisis se puedan silenciar si el usuario lo desea explícitamente. Por ejemplo:
>>> portfolio = parse_csv('missing.csv', types=[str,int,float], silence_errors=True)
>>> portfolio
[{'name': 'AA','shares': 100, 'price': 32.2}, {'name': 'IBM','shares': 50, 'price': 91.1}, {'name': 'CAT','shares': 150, 'price': 83.44}, {'name': 'GE','shares': 95, 'price': 40.37}, {'name': 'MSFT','shares': 50, 'price': 65.1}]
>>>
El manejo de errores es una de las cosas más difíciles de hacer bien en la mayoría de los programas. Como regla general, no debes ignorar silenciosamente los errores. En cambio, es mejor informar sobre los problemas y dar a los usuarios la opción de silenciar el mensaje de error si lo eligen.
¡Felicitaciones! Has completado el laboratorio de comprobación de errores. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.