Las anotaciones en Python son una herramienta que permite agregar información adicional sobre las variables, funciones y métodos que programamos. Aunque no restringen el tipo de datos que se puede almacenar en una variable, proporcionan un contexto más claro sobre el objetivo del código, beneficiando la legibilidad y comprensión, especialmente en entornos de trabajo en equipo.
¿Cómo se utilizan las anotaciones en las variables?
Las anotaciones para las variables consisten en especificar el tipo esperado usando dos puntos seguidos de la declaración del tipo. Por ejemplo, al crear un identificador para empleados, podríamos especificarlo de la siguiente manera:
id1:int=101id2:int=102
Al realizar operaciones, también podemos anotar el tipo de resultado:
total_id:int= id1 + id2
¿Cómo se implementan en funciones y métodos?
En las funciones, las anotaciones ayudan a definir los tipos de datos que los parámetros deben recibir y el tipo de dato que devolverá la función. La sintaxis se asemeja a esto:
En este ejemplo, se especifica que los parámetros id1 y id2 deben ser enteros y que el resultado también será un entero.
¿Cómo usar anotaciones en clases?
Al trabajar con clases, las anotaciones ayudan a definir tipos de los atributos y métodos. Veamos un ejemplo de una clase Empleado:
classEmpleado:def__init__(self, nombre:str, edad:int, salario:float): self.nombre = nombre
self.edad = edad
self.salario = salario
defpresentarse(self)->str:returnf"Hola, me llamo {self.nombre}. Tengo {self.edad} años."
Aquí se utilizan anotaciones para definir los tipos de parámetros esperados en el constructor y el tipo de dato que devuelve el método presentarse.
¿Cómo verificar tipos de datos usando librerías?
Además de las anotaciones, es posible utilizar herramientas como MyPy para analizar estáticamente el tipo de datos en un archivo Python, buscando posibles inconsistencias o errores. MyPy no es parte de la biblioteca estándar de Python, por lo que necesitas instalarlo:
pip install mypy
Luego, puedes usarlo para analizar un archivo:
mypy mi_archivo.py
¿Qué son Optional y Union?
Las librerías Optional y Union permiten mayor flexibilidad en las anotaciones al trabajar con funciones que pueden manejar múltiples tipos de datos o retornar None.
Optional: Indica que un valor podría ser del tipo especificado o None.
from typing import Optional
defencontrar_empleado(ids:list[int], id_buscado:int)-> Optional[int]:if id_buscado in ids:return id_buscado
returnNone
Union: Se usa cuando necesitas aceptar múltiples tipos de datos específicos, pero no None.
from typing import Union
defprocesar_salario(salario: Union[int,float])->float:returnfloat(salario)
Retos y recomendaciones prácticas
Te animamos a aplicar estos conceptos creando una función que procese diccionarios de empleados y los filtre según el salario. Experimenta con diferentes tipos de anotaciones y comparte tus resultados en los comentarios.
¡Continúa explorando y experimentando con Python! Las anotaciones son solo el comienzo para lograr un código más entendible y fácil de mantener.
Si bien las anotaciones de tipo solo tienen el propósito de indicar de forma explícita la naturaleza de las variables que estamos utilizando, lo cierto es que en su estructura podemos observar la forma en la que comúnmente se especifica el Dominio y el Contradominio de una función:
Excelente dato
def lista_empleados(lista_empleados:list,limite_salario:float)-> list:return[emp['nombre']for emp in lista_empleados if emp['sueldo']> limite_salario]lista =[{"nombre":"Maria La del Barrio","edad":30,"sueldo":30000},{"nombre":"Luis Miguel","edad":25,"sueldo":25000},{"nombre":"Ana Bolena","edad":20,"sueldo":20000},{"nombre":"Pepe Grillo","edad":20,"sueldo":50000}]print(lista_empleados(lista,25000))```def lista\_empleados(lista\_empleados:list, limite\_salario:float)-> list:return \[emp\['nombre']for emp in lista\_empleados if emp\['sueldo']> limite\_salario]lista = \[{"nombre":"Maria La del Barrio","edad":30,"sueldo":30000},{"nombre":"Luis Miguel","edad":25,"sueldo":25000},{"nombre":"Ana Bolena","edad":20,"sueldo":20000},{"nombre":"Pepe Grillo","edad":20,"sueldo":50000}]print(lista\_empleados(lista,25000))
Excelente!
Bueno, terminé de ver esta clase y algo que no me quedaron claro es ¿para que se usan las anotaciones?, probando con el código en Visual Studio Code, me di cuenta que precisamente sirve para dar información de que tipo de variables se deben introducir cuando se crea una función, clase, etc. y ya, porque puedo haver:
a: int = "Esto es una string"
y sigue funcionando, es decir, no aletara para nada la ejecución del código, pero al crar una función y luego ir a llamarla, cuando aparece el comentario o guía de los parámetros que hay que introducir, me indica qué tipo de variable se espera que introduzca, aunque puede uno colocar la que quiera. Lo cual es genial, porque al crear proyectos otra persona al ir a usar alguna función, clase o similar, tiene una clara guía de cómo el que creó ese código espera que sea empleado. Esto lo deduje yo mismo indagando al hacer pruebas.
Por favor corríganme si me equivoco, y si estoy en lo cierto, para cuando actualicen esta clase recomiendo que se explique precisamente eso, cuál es el proposito de las anotaciones, porque con esta clase me quedó super claro es cómo realizarlas y hacer código con ellas, pero al no ser consiente de su importancia, el cerebro lo ve como información "basura".
Perdón por los errores ortográficos, lo hice con algo de afán.
sip, tienes toda la razon, mas que nada la "anotación de tipo" ayuda al editor de codigo a sugerir el tipo de parámetros que se espera, esto ayuda mucho al programador
no es redundante poner el tipo en el constructor si ya esta especificado en la clase?
los atributos de clase son distintos a los atributos de instancia. grave error por parte de la clase
Más que una redundancia es un error en el ejemplo, ya que está creando variables estáticas.
Los atributos declarados fuera del constructor son variables estáticas. Las inicializaciones dentro del constructor están realmente creando variables de instancia con el mismo nombre que las variables ya creadas. En consecuencia hay dos atributos con el nombre "name": el de la clase, que es igual para todos los objetos, y el del objeto en sí.
tuki
empleados =[{'nombre':'Carlos','edad':32,'sueldo':5000},{'nombre':'Ana','edad':28,'sueldo':3000},{'nombre':'Pedro','edad':45,'sueldo':1500},{'nombre':'Luisa','edad':35,'sueldo':6000},{'nombre':'Jorge','edad':22,'sueldo':2000}]def mostrar_todos(lista_de_empleados):print("--- Lista completa ---")for empleado inlista_de_empleados:print(f"{empleado['nombre']} gana ${empleado['sueldo']}")def filtro_empleados(lista_empleados, sueldo_minimo): empleados_vip =[]for empleado inlista_empleados:if empleado['sueldo']> sueldo_minimo: empleados_vip.append(empleado)return empleados_vip
mostrar_todos(empleados)ricos =filtro_empleados(empleados,3500)print("\n--- EMPLEADOS CON SUELDO ALTO (> 3500) ---")mostrar_todos(ricos)
import json
import os
from typing import List, TypedDict
# Definimos la estructura del diccionario
class Empleado(TypedDict):
nombre: str
edad: int
sueldo: float
NOMBRE_ARCHIVO = "empleados.json"
def cargar_datos() -> List[Empleado]:
"""Carga los empleados desde un archivo JSON si existe."""
if os.path.exists(NOMBRE_ARCHIVO):
try:
with open(NOMBRE_ARCHIVO, 'r', encoding='utf-8') as f:
return json.load(f)
except (json.JSONDecodeError, IOError):
return []
return []
def guardar_datos(lista: List[Empleado]) -> None:
"""Guarda la lista de empleados en un archivo JSON."""
try:
with open(NOMBRE_ARCHIVO, 'w', encoding='utf-8') as f:
Las librerías Optional y Union en Python se utilizan para las anotaciones de tipo, permitiendo especificar múltiples tipos que una variable puede aceptar.
Optional: Indica que un valor puede ser del tipo especificado o None. Por ejemplo, Optional[int] significa que la variable puede ser un entero o None.
Union: Permite especificar que una variable puede ser de múltiples tipos. Por ejemplo, Union[int, float] indica que la variable puede ser un entero o un flotante.
Estas herramientas mejoran la claridad del código y ayudan en la validación de tipos.
Como anecdota, comentar que a partir de la version 3.10 ya está integrado este concepto con una sintaxis mucho más clara y pytonesca usando :
int | float
y en el caso de Optional:
int |None
# Implementa una funcion que procese una lista de diccionarios con informacion de empleados, utilizando anotaciones de tipo.#1 recibira una lista de diccionarios, cada diccionario tendra las claves: "nombre", "edad", "sueldo".#2 devolvera una lista de empleados que ganen mas de cierto sueldo.#importamos las librerias necesariasfrom typing import List, Dict
#creamos la funcion filtrar_empleados_por_sueldo que recibe una lista de diccionarios y un sueldo minimodeffiltrar_empleados_por_sueldo(empleados: List[Dict[str,int]], sueldo_minimo:float)-> List[Dict[str,int]]:return[empleado for empleado in empleados iffloat(empleado['sueldo'])> sueldo_minimo]#lista de diccionarios con informacion de empleadosempleados =[{'nombre':'Ana','edad':28,'sueldo':1500},{'nombre':'Luis','edad':35,'sueldo':2500},{'nombre':'Carlos','edad':40,'sueldo':3500},{'nombre':'Maria','edad':22,'sueldo':4500},{'nombre':'Jorge','edad':29,'sueldo':5500},{'nombre':'Laura','edad':31,'sueldo':6500},{'nombre':'Pedro','edad':38,'sueldo':7500},{'nombre':'Sofia','edad':26,'sueldo':8500},{'nombre':'Miguel','edad':33,'sueldo':9500},{'nombre':'Lucia','edad':27,'sueldo':10500}]#imprimimos la lista de empleados que ganan mas de 500print(filtrar_empleados_por_sueldo(empleados,7000))
me di cuenta que al copiar y pegar el símbolo de mayor (>) cambia por ">" pero no veo como editar o eliminar el comentario
Muchas gracias por tu aporte
from typing import Optional,Union
empleados =[{'nombre':'Carlos','edad':32,'sueldo':5000},{'nombre':'Ana','edad':28,'sueldo':3000},{'nombre':'Pedro','edad':45,'sueldo':1500},{'nombre':'Luisa','edad':35,'sueldo':6000},{'nombre':'Jorge','edad':22,'sueldo':2000.05}]defhigh_salaries(empleats:list[dict], limit: Union[int,float])->str: llista =[ empleado['nombre']for empleado in empleados if empleado['sueldo']> limit]return','.join(llista)print(high_salaries(empleados,2000))
Si usas ubuntu y te salta error con el comando
pip install mypy
usa estos comandos
1)instala las herramientas que necesitas para crear entornos virtuales
sudo apt update
sudo apt install python3-venv python3-full
2) ejecuta este comando en la carpeta del proyecto para crear el entorno virtual
python3 -m venv .venv
3) activa el entorno virtual
source .venv/bin/activate
se debe ver algo asi
4) ahora si usa el comando
pip install mypy
import json
from typing import Union, List, Dict, Any
Employee = Dict[str, Any]defemployee_union(salary: Union[int,float])->float:"""Convierte un salario (int o float) a entero.
return float(salary)"""deffilter_salary(lista_empleados: List[Employee])->None:"""Filtra y muestra empleados con salario mayor a 70,000."""# Mypy prefiere asignaciones directas en lugar de nonlocal si es posible most_salary =[p for p in lista_empleados if p['salary']>70000]for employee in most_salary: name = employee['name'] salary = employee_union(employee['salary'])print(f"Employee: {name}, Salary: {salary}")#Cargar datoswithopen('employees.json', mode='r')asfile: lista_empleados: List[Employee]= json.load(file)# Filtrar la listafilter_salary(lista_empleados)
importjsonfrom typing importUnion"""
Reto:Elabora un script con dos funciuones:1.Una funcion que reciba una lista de diccionarios.Cada diccionario tendrá las claves ->'nombre','edad' y 'sueldo'.2.Una funcion que devuelva una lista de empleados que ganen mas de cierto sueldo."""
path_empleados ='L:\\Mi unidad\\python\\employes.json'
#capturar empleados
withopen(path_empleados,'r')asdic_empleados: empleados = json.load(dic_empleados)#Generar lista de empleados con salario mayor al filtro
def procesar_salario(empleados:'dict',filtro_salario:Union[int, float])-> float: res =[]print(f'Los empleados con un salario superior a: {filtro_salario} son: ')for emp inempleados:if emp['salary']> filtro_salario: res.append(emp)return res
def mostrar_empleados(empleados:list[dict[str, str | int]]):for empleado inempleados:print(f'El empleado {empleado['name']}, tiene un salario de: {empleado['salary']}')sueldos_altos =procesar_salario(empleados,1500.5)mostrar_empleados(sueldos_altos)
Con las anotaciones...
# La función recibirá una lista de diccionarios.""""Cada diccionario tendrá las claves: `"name"`,
`"age"`, y `"salary"`."""# La función debe devolver una lista de empleados # que ganen de más de un cierto sueldo.import json
from typing import Union
from typing import TypedDict
# Clase EmployeeRowclassEmployeeRow(TypedDict): name:str age:int salary: Union[int,float] role:str# Clase EmployeeclassEmployee: name:str age:int salary: Union[int,float] role:strdef__init__(self, name:str, age:int, role:str, salary: Union[int,float])->None: self.name = name
self.age = age
self.role = role
self.salary = salary
def__repr__(self)->str:returnf"{self.name} - ({self.role}) - Salario: ${self.salary}"# Clase EmployeeManagerclassEmployeeManager:def__init__(self, employee_data:list[Employee])->None: self.employees = employee_data
# Método para filtrar empleados por salariodeffilter_by_salary(self, min_salary: Union[int,float])->list[Employee]:"""Retorna empleados con salario mayor a min_salary"""return[e for e in self.employees if e.salary > min_salary]# Se puedes agregar más filtros luego# def filter_by_role(self, role): ...""" importar listado de empleados de archivo externo. json"""file_path ="employee_data.json"withopen(file_path, mode='r', encoding='utf-8')asfile: employee_data:list[EmployeeRow]= json.load(file) employees:list[Employee]=[]# Convertir los dict del JSON en objetos Employeefor row in employee_data: e = Employee(name = row["name"], age = row["age"], role = row["role"], salary = row["salary"]) employees.append(e)# Mostrar lista globalprint("\n--- Global Employee List ---")for e in employees:print(e)# Filtrar empleados con salario > 5Mmanager = EmployeeManager(employees)filtered = manager.filter_by_salary(8000000)print("\n --- Employees earning > $8.000.000 ---")for e in filtered:print(e)
from typing import List
defsueldo_mayor_que(numero:int)-> List[int]: empleados =[{"nombre":"Ana García","edad":28,"sueldo":35000},{"nombre":"Carlos López","edad":35,"sueldo":42000},{"nombre":"María Rodríguez","edad":42,"sueldo":38000},{"nombre":"Juan Martínez","edad":31,"sueldo":45000},{"nombre":"Laura Fernández","edad":26,"sueldo":32000}]# Filtrar sueldos mayores que el número dado con lista de compresión sueldos_mayores =[empleado["sueldo"]for empleado in empleados if empleado["sueldo"]> numero]if sueldos_mayores:print(sueldos_mayores)else:print(f"No hay ningún sueldo mayor a {numero}")sueldo_mayor_que(39000)
from typing import List,Dict
defempl_salarios_altos(lista_de_empl: List[Dict[str,int|str]], salario_minimo:int)->List[Dict[str,int|str]]: empleados_filtrados=[empleado for empleado in lista_de_empl if empleado['salary']>salario_minimo]#Revisa en todo el diccionaro quien cumple la condicion return empleados_filtrados
defmain(): empl=[{'name':"Juan",'age':17,'salary':100},{'name':"Felipe",'age':15,'salary':25},{'name':"Carlos",'age':42,'salary':268},{'name':"Sara",'age':60,'salary':350},{'name':"Laura",'age':55,'salary':80}] empleados_filtrados=empl_salarios_altos(empl,100)print(f"{'Nombre':<10}{'Edad':<5}{'Salario':<7}\n{("-")*25}")#Organiza de forma elegante en una tablafor i in empleados_filtrados:print(f"{i['name']:<10}{i['age']:<10}{i['salary']:<10}")main()
from typing import List, Dict, Union
empleados: List[Dict[str, Union[int,str]]]=[]defcrear_empleados()->None:global empleados
empleados =[{"nombre":"Ana","salario":2500,"edad":28},{"nombre":"Luis","salario":3000,"edad":35},{"nombre":"Marta","salario":2500,"edad":41},{"nombre":"Carlos","salario":4000,"edad":30},]print("Lista de empleados creada.")defbuscar_por_sueldo(sueldo:int)-> List[Dict[str, Union[int,str]]]: resultado: List[Dict[str, Union[int,str]]]=[ emp for emp in empleados if emp["salario"]== sueldo
]return resultado
def employee_list(employee_list: list,target_salary: int)-> list:return[(employee["name"], employee["salary"])for employee in employee_list if employee["salary"]> target_salary]list =[{"name":"David","age":42,"salary":4500},{"name":"Leo","age":39,"salary":3500},{"name":"Sofia","age":32,"salary":5500}]print(employee_list(list,4000))