OpenWebinars

Lenguajes de Programación

Dominando Python: Estructuras de datos y POO

Si alguna vez has dudado de tu capacidad para programar, debes conocer Python, un lenguaje ideal para convertir la duda en confianza. En este artículo podrás conocer cómo las estructuras de datos y la POO son la base, no solo de crear código, sino para a pensar como un verdadero programador ante cualquier desafío de programación.

Pablo Huet

Pablo Huet

Experto Frontend

Lectura 11 minutos

Publicado el 16 de marzo de 2024

Compartir

Introducción

¿Te has preguntado alguna vez cómo los expertos en Python gestionan datos complejos o construyen aplicaciones robustas con facilidad?

Este artículo te muestra qué son las estructuras de datos y la Programación Orientada a Objetos (POO) en Python, de una forma práctica.

Desde dominar listas y diccionarios hasta crear clases y objetos sofisticados paso a paso, conocerás conceptos clave que son la base de la programación en Python.

Python, con su versatilidad y simplicidad, se ha consolidado como uno de los lenguajes de programación más populares y accesibles para desarrolladores de todos los niveles. Su potencia no solo reside en la facilidad de escritura y lectura del código, sino también en la profundidad de sus capacidades, particularmente en lo que respecta a la manipulación y la flexibilidad a la hora de elegir un estilo de programación.

Si quieres profundizar más en Qué es Python, en este artículo te contamos más sobre este lenguaje de programación, su instalación y primeros pasos de uso.

Estructuras de datos en Python

Python ofrece una variedad de estructuras de datos incorporadas, cada una con sus propias características y usos específicos. A continuación, exploramos las estructuras más fundamentales disponibles en Python y cómo pueden ser utilizadas para resolver diferentes problemas de programación.

Listas

Las listas en Python son colecciones ordenadas y mutables (es decir, son modificables una vez creadas) que pueden almacenar elementos de diferentes tipos al mismo tiempo. Son una de las estructuras de datos más versátiles y utilizadas en Python, ideales para almacenar una colección de datos que necesita ser modificada o accedida por índices.

mi_lista = [1, 'Python', True]
mi_lista.append(2) # Esto añade un nuevo elemento de valor 2, de forma que queda como [1, 'Python', True, 2]
mi_lista.pop() # Esto devuelve el último valor de la lista (2 en este caso) y lo elimina de la misma

Diccionarios

Los diccionarios son colecciones no ordenadas de pares clave-valor. Permiten almacenar datos de manera que cada elemento pueda ser rápidamente recuperado, actualizado o eliminado usando su clave. Son especialmente útiles para almacenar datos estructurados de forma eficiente.

mi_diccionario = {'nombre': 'Ana', 'edad': 30}
# Acceso al diccionario
print(mi_diccionario['nombre']) # Esto mostrará la cadena "Ana"

Tuplas

Las tuplas son colecciones ordenadas e inmutables. Se utilizan para almacenar una secuencia de valores que no deben cambiar a lo largo del programa. Son más rápidas que las listas y protegen los datos de modificaciones accidentales.

mi_tupla = (1, 'Python', True)
mi_tuple[0] = 2 # Esto dará un error, ya que no es mutable

Conjuntos

Los conjuntos o sets son colecciones desordenadas de elementos únicos. Son ideales para realizar operaciones de conjunto como uniones, intersecciones y diferencias, así como para eliminar elementos duplicados de una colección.

mi_conjunto = {1, 2, 3, 4, 5}
mi_conjunto.add(6) # El set añadirá un 6 y quedará como {1, 2, 3, 4, 5, 6}
mi_conjunto.add(5) # El set ya tenía un 5 así que seguirá como {1, 2, 3, 4, 5, 6}
mi_conjunto.remove(1) # El set queda como {2, 3, 4, 5, 6}

Colas y Pilas

Aunque Python no tiene estructuras de datos integradas específicamente para colas y pilas, las listas pueden ser usadas para implementar estas estructuras. Las pilas se manejan agregando y eliminando elementos del final de la lista, mientras que las colas pueden ser implementadas usando collections.deque para una gestión eficiente de elementos al inicio y al final.

from collections import deque
cola = deque(['Ana', 'Luis', 'Pedro'])

Comprender y elegir la estructura de datos adecuada es crucial para resolver problemas de manera eficiente en Python. Cada estructura tiene sus propias ventajas y limitaciones, y saber cuál utilizar puede marcar la diferencia en la eficacia y la claridad de tu código.

Con una sólida comprensión de estas estructuras de datos, los desarrolladores pueden abordar una amplia gama de desafíos de programación, desde la manipulación de datos simples hasta el diseño de algoritmos complejos.

Conviértete en un Backend Developer
Domina los lenguajes de programación más demandados. Accede a cursos, talleres y laboratorios para crear proyectos con Java, Python, PHP, Microsoft .NET y más
Comenzar gratis ahora

Programación Orientada a Objetos en Python

La Programación Orientada a Objetos (POO) es un paradigma fundamental en Python, que no solo enriquece el lenguaje con poderosas herramientas, sino que también facilita el desarrollo de software complejo y escalable.

A través de la POO, Python permite a los desarrolladores estructurar sus programas de manera que los objetos creados puedan interactuar entre sí de formas bien definidas, promoviendo así la reutilización de código y la modularidad.

Conceptos Clave de la POO en Python

Python implementa la POO a través de varios conceptos clave que forman la base general de este paradigma:

  • Clases: Las clases son los planos o moldes para crear objetos. Definen un conjunto de atributos (datos) y métodos (funciones) que caracterizan a cualquier objeto de ese tipo.
  • Objetos: Un objeto es una instancia de una clase. Cada objeto tiene un estado definido por sus atributos y comportamiento determinado por sus métodos. Los objetos son los actores fundamentales en programas orientados a objetos.
  • Herencia: La herencia permite crear nuevas clases que reutilizan, extienden o modifican el comportamiento de las clases existentes. Facilita la creación de jerarquías de clases y promueve la reutilización de código.
  • Polimorfismo: El polimorfismo se refiere a la capacidad de utilizar una interfaz única para diferentes tipos de datos o clases. En Python, permite que funciones o métodos puedan procesar objetos de diferentes clases que comparten la misma interfaz.
  • Encapsulación: Este principio oculta los detalles internos de cómo una clase implementa sus características, exponiendo solo lo que es necesario para el uso de la clase. La encapsulación protege los datos de un objeto contra acceso no autorizado y facilita la modificación del comportamiento interno sin afectar a quienes usan la clase.

Aplicaciones Prácticas

La POO en Python se utiliza en una amplia gama de aplicaciones, desde el desarrollo de interfaces gráficas de usuario (GUI) hasta la programación de sistemas de gestión de bases de datos y aplicaciones web. Su flexibilidad y potencia la hacen ideal para:

  • Diseñar sistemas complejos descomponiéndolos en componentes más pequeños y manejables (objetos).
  • Implementar patrones de diseño que solucionan problemas comunes en el desarrollo de software.
  • Facilitar la mantenibilidad y escalabilidad de los proyectos de software permitiendo actualizaciones y mejoras con un impacto mínimo en el código existente.

Principios básicos de la POO

La Programación Orientada a Objetos (POO) en Python se fundamenta en cuatro principios básicos que juntos facilitan el diseño de software robusto, escalable y fácil de mantener.

Encapsulación

La encapsulación se refiere a la práctica de ocultar los detalles internos de la implementación de un objeto y exponer solo aquellos componentes que sean seguros y necesarios para el uso externo.

Esto se logra a través del uso de métodos (funciones dentro de clases) que actúan como interfaz pública de un objeto. La encapsulación mejora la seguridad y la modularidad del código, permitiendo que los cambios internos se realicen sin afectar otras partes del programa.

Abstracción

La abstracción implica enfocarse en las características esenciales de un objeto, ignorando detalles irrelevantes o accidentales.

En la POO, las clases proporcionan una forma de abstraer objetos del mundo real, definiendo sus atributos esenciales y comportamientos. La abstracción ayuda a reducir la complejidad del diseño, permitiendo a los desarrolladores concentrarse en interacciones a alto nivel entre objetos.

Herencia

La herencia es un mecanismo que permite a una nueva clase heredar atributos y métodos de una clase existente.

La clase original se conoce como la clase base o superclase, mientras que la nueva clase se denomina subclase. La herencia promueve la reutilización de código y establece una relación jerárquica entre clases, facilitando la creación de estructuras de objetos complejas.

Polimorfismo

El polimorfismo permite que objetos de diferentes clases sean tratados como instancias de una clase común si comparten la misma interfaz (es decir, un conjunto de métodos públicos).

Esto significa que una función puede utilizar objetos de diferentes clases si todos ellos implementan un método específico. El polimorfismo aumenta la flexibilidad y la interoperabilidad del código, permitiendo diseñar sistemas más genéricos y extensibles.

Estos cuatro principios forman el núcleo de la POO en Python, proporcionando un marco práctico para estructurar programas de manera que mejoren la mantenibilidad, la escalabilidad y la eficiencia.

Al dominar la POO, los desarrolladores pueden aprovechar plenamente las capacidades de Python para crear soluciones sofisticadas y elegantes a problemas complejos.

Definición y uso de clases y objetos en Python

En Python, las clases y los objetos son los pilares fundamentales de la Programación Orientada a Objetos (POO). Una clase actúa como un plano para la creación de objetos, definiendo los atributos (datos) y métodos (funciones) que los objetos creados a partir de la clase podrán utilizar.

Los objetos son instancias de estas clases; cada objeto puede tener atributos distintos definidos en la clase, permitiendo a los desarrolladores modelar entidades del mundo real de manera efectiva y eficiente.

Creación de Clases

Para definir una clase en Python, se utiliza la palabra clave class, seguida del nombre de la clase y dos puntos. Dentro de la clase, se definen funciones que actúan como métodos para los objetos de la clase. Un método especial, __init__(), se utiliza como constructor para inicializar los atributos del objeto cuando se crea una instancia de la clase.

class Persona:
    def __init__(self, nombre, edad):
        self.nombre = nombre
        self.edad = edad

    def saludar(self):
        print(f'Hola, mi nombre es {self.nombre} y tengo {self.edad} años.')

Instanciación de Objetos

Crear un objeto a partir de una clase se conoce como instanciación. Esto se hace llamando al nombre de la clase como si fuera una función, pasando los argumentos requeridos por el método __init__.

persona1 = Persona('Ana', 30)
persona1.saludar()  # Salida: Hola, mi nombre es Ana y tengo 30 años.

Uso de Atributos y Métodos

Una vez que se ha creado un objeto, se puede acceder a sus atributos y métodos utilizando la notación de punto. Esto permite modificar los atributos del objeto y llamar a sus métodos, respectivamente.

# Accediendo a un atributo
print(persona1.nombre)  # Salida: Ana

# Llamando a un método
persona1.saludar()

La definición y uso de clases y objetos en Python facilitan la abstracción y encapsulación, permitiendo a los desarrolladores trabajar con conceptos complejos de manera más intuitiva y organizar mejor su código.

Al emplear estas estructuras, se pueden crear programas modulares y reutilizables que reflejen las entidades y comportamientos del problema a resolver de forma clara y eficiente.

Atributos y métodos en Python

En la Programación Orientada a Objetos (POO) con Python, los atributos y métodos son componentes esenciales de las clases, proporcionando la estructura y el comportamiento que los objetos de esas clases tendrán. A continuación, se detalla cómo se definen y utilizan dentro del paradigma de POO.

Definición de Atributos

Los atributos son variables que se asocian con una clase. Almacenan datos que son importantes para los objetos creados a partir de esa clase. Los atributos se definen dentro del método constructor __init__ de la clase, utilizando el prefijo self para indicar que pertenecen a instancias de la clase.

class Coche:
    def __init__(self, marca, modelo, año):
        self.marca = marca
        self.modelo = modelo
        self.año = año

Definición de Métodos

Los métodos son funciones que se definen dentro de una clase y están diseñados para operar en los datos (atributos) que los objetos de la clase contienen. Al igual que los atributos, se utilizan con el prefijo self para acceder a las variables y otros métodos del objeto.

class Coche:
    def __init__(self, marca, modelo, año):
        self.marca = marca
        self.modelo = modelo
        self.año = año

    def descripcion(self):
        return f'{self.marca} {self.modelo} de {self.año}'

    def es_antiguo(self):
        return 2023 - self.año > 20

Creación de Clases con Atributos y Métodos

Para utilizar estas definiciones, primero se crea una instancia de la clase y luego se accede a sus atributos y métodos utilizando la notación de punto. Esto permite no solo almacenar datos específicos en cada objeto sino también realizar operaciones o acciones específicas definidas por los métodos.

mi_coche = Coche('Toyota', 'Corolla', 1999)
print(mi_coche.descripcion())  # Salida: Toyota Corolla de 1999
print(mi_coche.es_antiguo())   # Salida: True

La combinación de atributos y métodos en las clases proporciona una manera poderosa de modelar entidades del mundo real, permitiendo que el código sea más organizado, reutilizable y fácil de mantener.

Al encapsular datos y funcionalidades específicas dentro de objetos, Python facilita el desarrollo de software complejo, haciéndolo más accesible y manejable.

Herencia y polimorfismo

La herencia y el polimorfismo son dos conceptos fundamentales en la Programación Orientada a Objetos (POO) que Python implementa de manera eficiente, permitiendo a los desarrolladores crear código reutilizable y mantener una estructura de código limpia y comprensible.

Herencia

La herencia permite a una clase heredar atributos y métodos de otra, facilitando la reutilización de código y la creación de relaciones jerárquicas entre clases. En Python, se define una clase base (o superclase) y una o más clases derivadas (o subclases) que heredan de la clase base.

class Vehiculo:
    def __init__(self, marca, modelo):
        self.marca = marca
        self.modelo = modelo

    def mostrar_informacion(self):
        print(f'Marca: {self.marca}, Modelo: {self.modelo}')

class Coche(Vehiculo):
    def __init__(self, marca, modelo, tipo):
        super().__init__(marca, modelo)
        self.tipo = tipo

    def mostrar_informacion(self):
        super().mostrar_informacion()
        print(f'Tipo: {self.tipo}')

La función super() se utiliza para llamar al constructor de la clase base, permitiendo que la clase derivada herede todos los atributos y métodos de la clase base y añada o modifique los necesarios para su propia implementación.

Polimorfismo

El polimorfismo se refiere a la capacidad de utilizar una interfaz única para objetos de diferentes clases. Python permite que diferentes clases tengan métodos con el mismo nombre, pero con implementaciones específicas para cada clase. Esto significa que un método puede comportarse de manera diferente dependiendo del objeto que lo invoque.

def imprimir_informacion_vehiculo(vehiculo):
    vehiculo.mostrar_informacion()

mi_coche = Coche('Toyota', 'Corolla', 'Sedán')
mi_vehiculo = Vehiculo('Honda', 'Civic')

imprimir_informacion_vehiculo(mi_coche)
imprimir_informacion_vehiculo(mi_vehiculo)

En el ejemplo anterior, la función imprimir_informacion_vehiculo puede aceptar cualquier objeto que tenga un método mostrar_informacion, demostrando cómo el polimorfismo permite tratar objetos de diferentes clases de manera uniforme.

La combinación de herencia y polimorfismo en Python facilita la creación de estructuras de código flexibles y extensibles, donde las nuevas funcionalidades pueden ser añadidas o modificadas con facilidad, manteniendo al mismo tiempo la claridad y la cohesión del código.

Encapsulación y abstracción

La encapsulación y la abstracción son dos principios fundamentales en la Programación Orientada a Objetos (POO) que trabajan conjuntamente para mejorar la seguridad y la estructura del código en Python, lo que permite a los desarrolladores diseñar sus programas de forma que oculten detalles internos y resalten las operaciones importantes.

Encapsulación

La encapsulación se refiere al agrupamiento de datos (atributos) y métodos que operan sobre esos datos dentro de una clase, protegiendo la información y ocultando los detalles de implementación del exterior.

En Python, esto se logra mediante el uso de métodos de acceso (getters y setters) y modificando el acceso a atributos y métodos mediante prefijos, como el subrayado simple (_) o doble (__), para indicar que son de uso interno.

class CuentaBancaria:
    def __init__(self, titular, saldo=0):
        self.titular = titular
        self.__saldo = saldo

    def depositar(self, cantidad):
        if cantidad > 0:
            self.__saldo += cantidad
            print(f"Depósito exitoso: {cantidad}")
        else:
            print("El depósito debe ser mayor que cero.")

    def mostrar_saldo(self):
        print(f"Saldo actual: {self.__saldo}")

En el ejemplo, __saldo es un atributo privado, accesible y modificable solo dentro de la clase CuentaBancaria mediante métodos específicos, como depositar y mostrar_saldo.

Abstracción

La abstracción, por otro lado, implica enfocarse en las operaciones relevantes de un objeto, ignorando los detalles menos importantes. En la POO, se logra definiendo clases abstractas y métodos que actúan como plantillas para otras clases.

Python implementa la abstracción mediante módulos como abc (Abstract Base Classes).

from abc import ABC, abstractmethod

class FiguraGeometrica(ABC):
    @abstractmethod
    def area(self):
        pass

class Circulo(FiguraGeometrica):
    def __init__(self, radio):
        self.radio = radio

    def area(self):
        return 3.1416 * self.radio**2

En este caso, FiguraGeometrica define una estructura abstracta que otras clases, como Circulo, deben implementar. Esto asegura que todas las subclases de FiguraGeometrica tengan un método area, aunque su implementación varíe.

Técnicas para implementarlos

Para implementar la encapsulación y la abstracción se pueden seguir estas técnicas:

  • Utilizar métodos de acceso para leer o modificar atributos privados.
  • Emplear clases y métodos abstractos para definir plantillas para otras clases.
  • Ocultar detalles de implementación que no sean necesarios para el uso externo de la clase.

Aplicaciones prácticas en el desarrollo del software

La encapsulación y la abstracción son esenciales para el desarrollo de software seguro y mantenible. Permiten crear interfaces claras, reducir la complejidad del código y proteger los datos sensibles, facilitando la extensión y modificación del software sin comprometer su integridad.

Estos principios, cuando se aplican correctamente, contribuyen a la creación de sistemas de software robustos y flexibles, donde los cambios y mejoras pueden implementarse de manera eficiente y segura.

Manejo de excepciones

El manejo de excepciones es una parte crítica de la programación en Python, especialmente a la hora de implementar un sistema orientado a objetos.

Esto permite a los desarrolladores controlar errores de manera elegante y mantener la robustez y estabilidad de sus aplicaciones mediante la manipulación de las excepciones, que son errores detectados durante la ejecución del programa, y el manejo adecuado de las mismas.

Try y Except

Python utiliza bloques try y except para capturar y manejar excepciones. El código que puede generar una excepción se coloca dentro del bloque try, y el código para manejar la excepción se escribe en el bloque except.

Python intenta ejecutar el código en try, y si ocurre una excepción, ejecuta el código en except.

try:
    resultado = 10 / 0
except ZeroDivisionError:
    print('Error: División por cero.')

Finally

El bloque finally se utiliza para ejecutar código que debe ejecutarse siempre, independientemente de si ocurrió una excepción o no. Es útil para realizar tareas de limpieza, como cerrar archivos o liberar recursos externos.

try:
    archivo = open('archivo.txt')
    # Realizar operaciones con el archivo
except IOError:
    print('Error al abrir el archivo.')
finally:
    archivo.close()

Levantar Excepciones

Python también permite a los desarrolladores “levantar” excepciones utilizando la palabra clave raise. Esto es útil cuando se necesita forzar una excepción si cierta condición no se cumple.

def dividir(a, b):
    if b == 0:
        raise ValueError('El divisor no puede ser cero.')
    return a / b

El concepto de “levantar” lo que nos permite es lanzar este error de forma que pueda ser capturado en un bloque superior que invoque a esta función:

try:
    dividir(10, 0)
except ValueError as e:
    print(e) # Aparecerá por pantalla "El divisor no puede ser cero."

Excepciones Personalizadas

Para casos más específicos, se pueden definir excepciones personalizadas. Estas son clases que derivan de la clase base Exception y permiten una mayor flexibilidad en el manejo de errores.

class ErrorDeValorNegativo(Exception):
    pass

def raiz_cuadrada(numero):
    if numero < 0:
        raise ErrorDeValorNegativo('El número no puede ser negativo.')
    return numero**0.5

El manejo de excepciones en Python no solo ayuda a prevenir fallos en las aplicaciones, sino que también facilita la depuración y el mantenimiento del código, permitiendo a los desarrolladores anticipar y planificar la respuesta a condiciones de error. Adoptar un enfoque proactivo en el manejo de excepciones es fundamental para desarrollar software robusto y confiable.

Mejora las habilidades de tus desarrolladores
Acelera la formación tecnológica de tus equipos con OpenWebinars. Desarrolla tu estrategia de atracción, fidelización y crecimiento de tus profesionales con el menor esfuerzo.
Solicitar más información

Conclusiones

Las estructuras de datos como listas, diccionarios, tuplas y conjuntos son herramientas esenciales para cualquier desarrollador, facilitando la organización y manipulación de datos. Mientras tanto, la POO nos enseña a pensar en términos de objetos y clases, lo que nos ayuda a construir programas modulares y reutilizables.

La aplicación de conceptos inherentes a la orientación a objetos, como la herencia y el polimorfismo, amplían nuestras posibilidades de diseño, permitiéndonos crear sistemas más flexibles y escalables. Al mismo tiempo, otros como la encapsulación y la abstracción protegen la integridad de nuestros datos y simplifican la interacción con ellos.

Dominar estas áreas de Python no solo enriquece nuestro conjunto de habilidades, sino que también prepara el terreno para abordar proyectos más ambiciosos con confianza y creatividad. Con estos fundamentos, estamos listos para enfrentar los desafíos de la programación con soluciones innovadoras y eficaces.

Bombilla

Lo que deberías recordar de las estructuras de datos y POO en Python

  • Variedad y flexibilidad: Python ofrece diversas estructuras de datos, como listas para colecciones ordenadas, diccionarios para pares clave-valor, tuplas para datos inmutables y conjuntos para elementos únicos.
  • Eficiencia en la manipulación de datos: Elegir la estructura de datos correcta es fundamental para el rendimiento. Por ejemplo, los diccionarios son ideales para búsquedas rápidas por clave, mientras que las listas son excelentes para operaciones ordenadas.
  • Orientación a objetos: La Programación Orientada a Objetos se centra en el uso de clases y objetos para modelar elementos del mundo real. Las clases definen atributos y métodos, mientras que los objetos son instancias de estas clases.
  • Encapsulación y abstracción: La encapsulación protege los datos dentro de un objeto, y la abstracción reduce la complejidad al enfocarse en las operaciones esenciales, mejorando la claridad y la seguridad del código.
  • Herencia: La herencia permite que una clase herede características de otra, facilitando la reutilización de código y la creación de jerarquías de clases.
  • Polimorfismo: El polimorfismo permite que un mismo método se comporte de manera diferente en clases derivadas, proporcionando flexibilidad para manejar diversos tipos de objetos a través de una interfaz común.
  • Robustez del programa: Manejar adecuadamente las excepciones es esencial para construir aplicaciones robustas. Python permite capturar y responder a errores de manera controlada con bloques try y except, evitando fallos del programa y mejorando la experiencia del usuario ante situaciones inesperadas.
Compartir este post

También te puede interesar

Icono de la tecnología
Curso

Python 3 desde cero

Intermedio
6 h. y 8 min.

Descubre el fascinante mundo del lenguaje de programación que funciona como una auténtica navaja suiza cuando se trata...

José Domingo Muñoz
4.5