¿Cómo manejar excepciones y validar tipos en Python?
La validación de tipos y manejo de excepciones en Python es esencial, sobre todo en proyectos colaborativos o críticos donde los datos externos juegan un papel crucial. Aunque Python permite especificar tipos de datos para los parámetros de una función, no garantiza que estos sean correctos en tiempo de ejecución. Esta lección explora cómo validar tipos de datos y personalizar excepciones para asegurar un comportamiento consistente del código bajo diferentes circunstancias.
¿Cómo se crea una función que divide dos números?
Primero, vamos a crear una función que se encargará de dividir dos números enteros. El resultado de esta función será de tipo flotante. Aquí está el enfoque básico:
defdividir(a:int, b:int)->float:return a / b
Al llamar a la función dividir(10, 2), obtendremos el resultado esperado. Sin embargo, si ingresamos un string en lugar de un número entero, como dividir(10, '2'), obtendremos un error de tipo (type error).
¿Cómo validar que los parámetros sean enteros?
Para evitar errores de tipo, validamos que ambos parámetros sean enteros o flotantes antes de ejecutar la división:
defdividir(a, b):ifnotisinstance(a,int)ornotisinstance(b,int):raise TypeError("Ambos parámetros deben ser enteros")if b ==0:raise ValueError("El divisor no puede ser cero")return a / b
¿Cómo se manejan las excepciones con try y except?
El manejo de excepciones es crucial para dar más información sobre los errores y prevenir que el programa se detenga abruptamente. Aquí te muestro cómo manejar excepciones usando try y except:
try: resultado = dividir(10,'2')except TypeError as e:print(f"Error: {e}")try: resultado = dividir(10,0)except ValueError as e:print(f"Error: {e}")
Este enfoque permite capturar cualquier tipo de error y personalizar el mensaje de error según sea necesario, lo que mejora la depuración y la comprensión del error original.
¿Por qué es importante personalizar las excepciones?
El uso de excepciones personalizadas no solo brinda claridad sobre lo que ha salido mal, sino que también ofrece detalles específicos del error. Aquí un ejemplo de cómo podrías lanzar y personalizar excepciones:
defdividir(a, b):ifnotisinstance(a,int)ornotisinstance(b,int):raise TypeError("Ambos parámetros deben ser enteros")if b ==0:raise ValueError("El divisor no puede ser cero")return a / b
Cada vez que se lanza una excepción específica, puedes proporcionar un mensaje detallado que ayude a identificar el origen del problema sin necesidad de leer todo el stack trace.
¿Cuál es la importancia de las buenas prácticas en validación y manejo de errores?
Aplicar estas técnicas de validación y manejo de excepciones es fundamental para mantener un código limpio y robusto, especialmente en ambientes de desarrollo colaborativos. Algunas de las buenas prácticas incluyen:
Validar Entrada de Datos: Asegurar que los datos son del tipo esperado antes de procesarlos.
Manejo de Excepciones: Capturar y manejar de forma adecuada las excepciones para proporcionar mensajes de error informativos.
Personalización: Crear excepciones personalizadas para situaciones específicas mejorando la depurabilidad del código.
Estas prácticas no solo mejoran la estabilidad y mantenibilidad del código, sino que también facilitan la colaboración efectiva en proyectos de gran escala.
¡Continúa tu aprendizaje y sigue mejorando tus habilidades en programación! Entender y manejar las excepciones y tipos en Python te ayudará a escribir aplicaciones más seguras y fiables.
La clase está mal grabada. En el minuto 11:06, mientras explica la aplicación usando Try, de pronto salta, no apareciendo las líneas que antes había escrito.
si pz un error de edicion, continua en el min 12:35
Es correcto, acabo de reportarlo como bug de edición.
Muchas gracias
La validación de tipos en métodos en Python es una práctica que se utiliza para asegurar que los argumentos de las funciones o métodos tienen los tipos esperados. Esto puede ayudar a prevenir errores en tiempo de ejecución y facilitar la comprensión del código. A continuación, se presentan varias maneras de implementar la validación de tipos en métodos:
### 1. Anotaciones de Tipo
Desde Python 3.5, se pueden utilizar anotaciones de tipo para indicar qué tipos de datos se esperan como argumentos y qué tipo se devolverá. Sin embargo, estas anotaciones son solo sugerencias y no imponen la validación de tipos en tiempo de ejecución. Aún así, se pueden usar herramientas como mypy para comprobar las anotaciones de tipo.
defadd(a:int, b:int)->int:  return a + bprint(add(5,10))# Salida: 15
### 2. Validación Manual de Tipos
Si deseas realizar la validación de tipos en tiempo de ejecución, puedes hacerlo utilizando isinstance dentro del método.
defadd(a:int, b:int)->int:  if not isinstance(a, int) or not isinstance(b, int):  raise TypeError("Los argumentos deben ser enteros.")  return a + btry:  print(add(5, 10)) # Salida: 15  print(add(5, '10')) # Esto generará un TypeErrorexcept TypeError as e:  print(e) # Salida: Los argumentos deben ser enteros.
### 3. Uso de Decoradores
También puedes crear un decorador para manejar la validación de tipos en múltiples funciones o métodos, lo que hace que tu código sea más limpio y reutilizable.
deftype\_check(\*arg\_types):  def decorator(func):  def wrapper(\*args):  for a, t in zip(args, arg\_types):  if not isinstance(a, t):  raise TypeError(f"Argumento {a} no es de tipo {t.\_\_name\_\_}.")  return func(\*args)  return wrapper  return decorator@type\_check(int,int)defadd(a, b):  return a + btry:  print(add(5, 10)) # Salida: 15  print(add(5, '10')) # Esto generará un TypeErrorexcept TypeError as e:  print(e) # Salida: Argumento 10 no es de tipo int.
### 4. Librerías de Validación
Existen librerías externas como pydantic y attrs que pueden ayudarte a manejar la validación de tipos y la creación de clases de forma más robusta y concisa.
La validación de tipos es una práctica importante en Python para asegurar la calidad y la robustez del código. Puedes optar por usar anotaciones de tipo junto con herramientas de análisis estático, o bien implementar validaciones manuales o usar decoradores para garantizar que los métodos reciban los tipos de datos esperados.
La actividad de la clase anterior :
podran ver que importo el modulo string como strModule, esto porque inicialmente lo tenia como str, y al correr mypy, me estaba dando errores, pues tenia el mismo nombre que el tipo de dato (yo no habia caido en la cuenta)
import random as rand
import string as strModule
from typing import Union
"""this program recives from a data base of employes and filter by salary"""name:str='';defnames_generator()->str:global name #no es posible unificar estas dos linea de codigo name=' pendejas 'for i inrange(5): name+=(rand.choice(strModule.ascii_uppercase))return name
#for the sake of the example the data base is not in a file and is not pass as a parameterdatabase =[{'name':names_generator(),'age':rand.randint(18,68),'salary':rand.randint(5000,20000)}for _ inrange(300)]defloadData(data:Union[list,dict],treshold:int)->list:"""it lods the data from the data base""" dataToFilter= data # nunca se toca la db originaldefsalary_filter(treshhole)->list:"""it filters the data using nonlocal"""nonlocal dataToFilter
return[dic for dic in dataToFilter if dic['salary']>=treshhole]return salary_filter(1)print(loadData(database,12340))
Saludos, analizando tu código hice algunas modificaciones y mejoras para que sea mas legible, modular y aplicando las validaciones en la entrada de los datos.
hola @William Harrisson gracias por tu intencion, pero tu comentario no es accesible,(copiaste y pegaste un screen shot, sin alt text) aun cuando la seccion de comentarios permite pegar el codigo con formato como yo lo hice en mi comentario. por eso no es posible leer tu codigo con NVDA o cualquier otro lector de pantalla. Saludos.
hola. aca les dejo mi codigo implementando lo que se vio en la clase con el producto punto
from functools import reduce
def dotProduct(vector1:list,vector2:list)->list:iflen(vector1)!=len(vector2): raise ValueError('Vectors must be of the same length')result:list=[0]*len(vector1)for index,number inenumerate(vector1):if not isinstance(vector1[index],int) or not isinstance(vector2[index],int): raise TypeError('No int values in list, just able to operate int values ') result[index]=vector1[index]*vector2[index]returnreduce(lambda a,b: a+b, result)try: v=[1,2] u=[2,1]print(dotProduct(v,u))except(TypeError,ValueError)ase:print(f'error:{e}')
Muy bien, gracias
Mi aporte a esta clase:
from typing importUnionimport os
os.system('cls')def promedio(list_number:Union[list, tuple])->str:if not all(isinstance(x,(list, tuple))for x in list_number): raise TypeError("Todos los elementos deben ser numericos ⚠️") elif not isinstance(list_number,(list, tuple)): raise ValueError("Debe ser una lista o tupla ⚠️")else: prom =sum(list_number)/len(list_number)return f'Promedio General ∑: {prom}'try:print(promedio([1,'a',3]))except(ValueError,TypeError)ase:print(f'❗Error❗: {e}')
from typing importUnionimport os
# Limpia la consola(sólo aplica en sistemas Windows).os.system('cls')def promedio(list_number:Union[list, tuple])-> str:"""
Calcula el promedio aritmético de una lista o tupla de números.Parámetros:----------list_number: list | tuple
Una lista o tupla de elementos numéricos(int o float).Retorna:------- str
Un string con el promedio formateado con un mensaje descriptivo.Excepciones:-----------ValueError:Si el argumento no es una lista o tupla.TypeError:Si algún elemento dentro de la lista o tupla no es numérico.Nota:----Esta función está diseñada para recibir una colección de elementos numéricos
y retornar el promedio en formato de texto.No calcula sobre estructuras anidadas."""
# Validación del tipo del contenedor principal(lista o tupla)if not isinstance(list_number,(list, tuple)): raise ValueError("Debe ser una lista o tupla ⚠️") # Validación del tipo de cada elemento dentro de la colección
if not all(isinstance(x,(int, float))for x in list_number): raise TypeError("Todos los elementos deben ser numéricos (int o float) ⚠️") # Cálculo del promedio
prom =sum(list_number)/len(list_number)return f'Promedio General ∑: {prom:.2f}' # Resultado con 2 decimales
# Bloque de prueba para manejo de errores
try:print(promedio([1,'a',3])) # Esto debe lanzar un TypeErrorexcept(ValueError,TypeError)ase:print(f'❗Error❗: {e}')
Perfecto siempre me pregunte porque se usaba raise si con mandar el tipo de error era suficiente y veo que es para darle al usuario una razón especifica de porque salio mal la ejecución!
tengo unas dudas.
anotación de tipo escomo typescript de java pero para Python verdad? y significa que puede tener los mismos alcances? o solo como para especificación de tipos no mas?
def divide(a: int, b: int)->float:
En esta definicion solo estoy afirmando que los parametro de entradas deben ser enteros , no puedo decir que son enteros o flotantes ya que saldria error . la definicion es muy clara y adicionalmente especifica que la salida siempres sera flotante.
como se puede anidar el manejo de excepciones? si por ejemplo para el mismo codigo queremos que se guarde en e la excepcion que arroje pero sabemos que puede ser 3 tipos de errores diferentes?
#Ejemplo en una funcion
from typing importUniondef dividir(a:Union[int, float],b:Union[int, float])-> float:if not isinstance(a,(int, float)) or not isinstance(b,(int, float)): raise TypeError("Los argumentos deben ser números.")try:return a / b
except ZeroDivisionError:print("No se puede dividir entre cero.")returnfloat('inf')print(dividir(10,2))print(dividir(10,0))
def divide(a: int,b: int)-> float | str: # Validar que ambos parámetros sean enteros
if not isinstance(a, int) or not isinstance(b, int):return'Error: Both parameters must be integers'if b ==0:return"Error: The divisor can't be zero"return a / b
# 🧪 Casos de prueba
print(divide(10,'2')) # Error:Both parameters must be integers
print(divide(10,0)) # Error:The divisor can't be zero
print(divide(10,2)) # 5.0
Me compliqué mucho entendiendo esta clase, cuando el código se pudo simplicar de la sig manera: ```js
def divide(a: int, b: int) -> float | str:
# Validar que ambos parámetros sean enteros
if not isinstance(a, int) or not isinstance(b, int):
return 'Error: Both parameters must be integers'
if b ==0:return"Error: The divisor can't be zero"return a / b
print(divide(10, '2')) # Error: Both parameters must be integers
print(divide(10, 0)) # Error: The divisor can't be zero
print(divide(10, 2)) # 5.0
El error "no such file or directory" no está relacionado directamente con la función divide. Sin embargo, el problema principal es que estás intentando dividir un entero por una cadena ('2'), lo que genera un TypeError.
Para evitar este error, asegúrate de validar que ambos parámetros sean enteros. Puedes usar isinstance() para verificar el tipo de datos antes de realizar la operación. Aquí tienes una versión corregida de tu función:
defdivide(a:int, b:int)->float:ifnotisinstance(a,int)ornotisinstance(b,int):raise TypeError("Ambos parámetros deben ser enteros.")return a / b
print(divide(10,2))# Esto funcionará
Asegúrate de ejecutar tu código en un entorno adecuado para que no aparezca el error de archivo.
Python es un lenguaje dinámicamente tipado, osea que no es extrictamente necesario la validación de datos como en otros lenguajes, no se queden trabados con estas clases!
El video quedo mal editado en la parte del try pero no se asusten. Unos minutos más adelante retoman nuevamente.
Hola José,
Muchas gracias por reportarlo, ¡Ya lo arreglamos! c;
Excelente clase querida profesora
defdivide(a:int, b:int)->float:ifnotisinstance(a,int)andisinstance(b,int):raise TypeError('Los dos parametros deben ser números enteros.')if b ==0:raise ValueError('No se puede dividir por 0')return a/b
try:print("¡ BIENVENIDO ! Vamos a divivir las manzanas para los niños") a =int(input("Ingresa el numero de manzanas (disponibles): ")) b =int(input("Ingresa el numero de niños: "))print(f'A cada niño le tocan de a {divide(a, b)} manzana(s)')except(TypeError, ValueError)as e:print(f'Error: {e}')
Aunque Python proporciona mensajes de error predeterminados, es recomendable utilizarraise con mensajes personalizados para indicar de manera clara y específica qué error ocurrió. Esto mejora la comunicación, la legibilidad y la claridad del código, facilitando su mantenimiento y depuración.
¿Este tema está más relacionado a testing no?
Yo, crearía que es mejor usar:
assert type(sales) == list, "sales should be a list"
def divicion(a:int,b: int)-> float: #Validando que ambos parámetros sean enteros
if not isinstance(a, int) or not isinstance(b, int): raise Exception(f"Los números deben ser enteror o flotantes. A: { a }, B: { b }") #Validando que no sea cero el divisor
if b ==0: raise Exception("El divisor no puede ser cero 0")return a / b;try: resultado_uno =divicion(0,5)print(resultado_uno)except Exceptionase:print(f"Error { e }")try: resultado_uno =divicion(0,0)print(resultado_uno)except Exceptionase:print(f"Error { e }")try: resultado_uno =divicion(-21,21)print(resultado_uno)except Exceptionase:print(f"Error { e }")try: resultado_uno =divicion("-21",1)print(resultado_uno)except Exceptionase:print(f"Error { e }")```def divicion(a:int,b: int)-> float:*#Validando que ambos parámetros sean enteros*if not isinstance(a, int) or not isinstance(b, int): raise Exception(f"Los números deben ser enteror o flotantes. A: { a }, B: { b }")*#Validando que no sea cero el divisor*if b ==0: raise Exception("El divisor no puede ser cero 0")return a / b;try: resultado\_uno =divicion(0,5)print(resultado\_uno)except Exceptionase:print(f"Error { e }")try: resultado\_uno =divicion(0,0)print(resultado\_uno)except Exceptionase:print(f"Error { e }")try: resultado\_uno =divicion(-21,21)print(resultado\_uno)except Exceptionase:print(f"Error { e }")try: resultado\_uno =divicion("-21",1)print(resultado\_uno)except Exceptionase:print(f"Error { e }")