27- 馃З Polimorfismo en Python: Conceptos B谩sicos




Python Polymorphism

馃幁 Python Polymorphism: Muchas Formas, Una Acci贸n

El **polimorfismo** es un concepto fundamental en la programaci贸n orientada a objetos (POO) que significa "muchas formas". En el contexto de la programaci贸n, se refiere a la capacidad de diferentes objetos de responder al mismo m茅todo o funci贸n de manera espec铆fica a su tipo.

En esencia, el polimorfismo permite escribir c贸digo que puede funcionar con objetos de diferentes clases sin necesidad de conocer el tipo exacto de cada objeto en tiempo de ejecuci贸n. Esto conduce a un c贸digo m谩s flexible, reutilizable y f谩cil de mantener.

Tipos de Polimorfismo en Python:

Python implementa el polimorfismo de varias maneras, incluyendo:

  • Polimorfismo de funci贸n: Funciones que pueden operar con objetos de diferentes tipos.
  • Polimorfismo de clase (a trav茅s de la herencia): M茅todos en clases hijas que pueden sobrescribir o extender los m茅todos de sus clases padres, comport谩ndose de manera diferente.

Ejemplo Sencillo de Polimorfismo de Funci贸n: La Funci贸n len()

Una funci贸n incorporada en Python que demuestra el polimorfismo es len(). Esta funci贸n puede tomar diferentes tipos de objetos (como cadenas, listas, tuplas, diccionarios, conjuntos) y devuelve la "longitud" apropiada para cada tipo:

      
mi_cadena = "Hola"
mi_lista = [1, 2, 3, 4]
mi_tupla = (10, 20, 30)
mi_diccionario = {"a": 1, "b": 2}
mi_conjunto = {5, 6, 7}

print(len(mi_cadena))      # Output: 4 (n煤mero de caracteres)
print(len(mi_lista))       # Output: 4 (n煤mero de elementos)
print(len(mi_tupla))       # Output: 3 (n煤mero de elementos)
print(len(mi_diccionario)) # Output: 2 (n煤mero de pares clave-valor)
print(len(mi_conjunto))    # Output: 3 (n煤mero de elementos 煤nicos)
      
     

La funci贸n len() funciona de manera polim贸rfica porque la misma llamada de funci贸n (len()) produce resultados diferentes basados en el tipo del objeto que se le pasa como argumento. Cada tipo de objeto implementa internamente la l贸gica necesaria para calcular su propia longitud.

En los siguientes temas, exploraremos el polimorfismo con diferentes tipos de datos y a trav茅s de la herencia de clases.

Function Polymorphism

⚙️ Polimorfismo de Funci贸n: Una Funci贸n, M煤ltiples Comportamientos

El polimorfismo de funci贸n se manifiesta cuando una funci贸n puede operar sobre objetos de diferentes clases sin necesidad de conocer expl铆citamente el tipo de cada objeto. La clave para esto radica en que los objetos deben implementar los m茅todos que la funci贸n espera utilizar, aunque la implementaci贸n de esos m茅todos puede variar entre las clases.

Ejemplo: Una Funci贸n para Presentar Objetos

Consideremos una funci贸n simple llamada presentar() que toma un objeto como argumento y llama a un m茅todo llamado hablar() en ese objeto. Diferentes clases de objetos pueden tener su propia implementaci贸n del m茅todo hablar(), lo que resulta en un comportamiento polim贸rfico de la funci贸n presentar().

      
class Gato:
    def hablar(self):
        print("¡Miau!")

class Perro:
    def hablar(self):
        print("¡Guau!")

class Pato:
    def hablar(self):
        print("¡Cuac!")

def presentar(animal):
    animal.hablar()

# Crear instancias de las clases
mi_gato = Gato()
mi_perro = Perro()
mi_pato = Pato()

# Llamar a la misma funci贸n con diferentes objetos
presentar(mi_gato)  # Output: ¡Miau!
presentar(mi_perro) # Output: ¡Guau!
presentar(mi_pato)  # Output: ¡Cuac!
      
     

En este ejemplo, la funci贸n presentar() no sabe si el objeto animal es un Gato, un Perro o un Pato. Sin embargo, como cada una de estas clases implementa un m茅todo llamado hablar(), la funci贸n presentar() puede llamar a ese m茅todo en cualquier objeto de estas clases, y cada objeto responder谩 de acuerdo a su propia implementaci贸n.

El Poder del Polimorfismo de Funci贸n:

  • Flexibilidad: Permite que el c贸digo funcione con una variedad de objetos.
  • Reusabilidad: La misma funci贸n se puede utilizar para diferentes tipos, reduciendo la necesidad de escribir funciones espec铆ficas para cada tipo.
  • Extensibilidad: Se pueden a帽adir nuevas clases que implementen el m茅todo esperado por la funci贸n, y la funci贸n seguir谩 funcionando con los objetos de estas nuevas clases sin necesidad de modificaci贸n.

Polimorfismo con Operadores:

Otro ejemplo de polimorfismo de funci贸n en Python se ve con los operadores. Por ejemplo, el operador + puede realizar diferentes acciones dependiendo de los tipos de operandos:

      
print(2 + 3)       # Output: 5 (suma de n煤meros)
print("Hola" + " " + "Mundo") # Output: Hola Mundo (concatenaci贸n de cadenas)
print([1, 2] + [3, 4])     # Output: [1, 2, 3, 4] (concatenaci贸n de listas)
      
     

El operador + est谩 sobrecargado (internamente implementado de forma diferente) para trabajar con n煤meros, cadenas y listas, mostrando un comportamiento polim贸rfico.

En los siguientes temas, exploraremos ejemplos de polimorfismo con diferentes tipos de datos incorporados en Python.

String

馃У Polimorfismo con Cadenas (str)

Las cadenas en Python son secuencias inmutables de caracteres y soportan varias operaciones que demuestran polimorfismo, especialmente cuando interact煤an con funciones y operadores que tambi茅n funcionan con otros tipos de secuencia.

Ejemplo: La Funci贸n len()

Como vimos en la introducci贸n al polimorfismo, la funci贸n len() se comporta de manera polim贸rfica con las cadenas, devolviendo el n煤mero de caracteres que contiene la cadena.

      
mi_string = "Python"
print(len(mi_string)) # Output: 6
      
     

La misma funci贸n len() se utiliza para obtener la longitud de listas, tuplas, etc., mostrando su naturaleza polim贸rfica.

Ejemplo: El Operador + (Concatenaci贸n)

El operador +, cuando se utiliza con cadenas, realiza la operaci贸n de concatenaci贸n, uniendo dos o m谩s cadenas en una nueva cadena.

      
parte1 = "Hola"
parte2 = "Mundo"
saludo = parte1 + " " + parte2
print(saludo) # Output: Hola Mundo
      
     

Este mismo operador + se comporta de manera diferente con n煤meros (suma) y listas/tuplas (concatenaci贸n de secuencias), lo que tambi茅n es un ejemplo de polimorfismo de operador.

Ejemplo: M茅todos Comunes a Secuencias

Aunque las cadenas tienen sus propios m茅todos espec铆ficos, tambi茅n comparten algunos m茅todos conceptualmente similares a otras secuencias (como listas y tuplas), aunque la implementaci贸n interna sea diferente. Un ejemplo podr铆a ser la idea de "contener" elementos (caracteres en el caso de cadenas).

      
mi_string = "abcdefg"
print('c' in mi_string) # Output: True
print('z' in mi_string) # Output: False

mi_lista = [1, 2, 3, 4]
print(3 in mi_lista)   # Output: True
print(5 in mi_lista)   # Output: False
      
     

El operador in funciona con diferentes tipos de secuencias (cadenas y listas en este caso) para verificar la pertenencia de un elemento, mostrando un comportamiento polim贸rfico a nivel de operador.

La Naturaleza Inmutable de las Cadenas:

Es importante recordar que las cadenas en Python son inmutables. Las operaciones que parecen modificar una cadena (como la concatenaci贸n o el uso de m茅todos como replace()) en realidad crean una nueva cadena en lugar de modificar la original. Este comportamiento es espec铆fico del tipo str.

En el siguiente tema, veremos c贸mo el polimorfismo se aplica a las tuplas en Python.

Tuple

馃П Polimorfismo con Tuplas (tuple)

Las tuplas en Python son secuencias inmutables de elementos. Al igual que las cadenas y las listas, las tuplas exhiben polimorfismo al interactuar con funciones y operadores dise帽ados para trabajar con secuencias.

Ejemplo: La Funci贸n len()

La funci贸n len() funciona de manera polim贸rfica con las tuplas, devolviendo el n煤mero de elementos que contiene la tupla.

      
mi_tupla = (1, 2, 3, "a", "b")
print(len(mi_tupla)) # Output: 5
      
     

Esta misma funci贸n, como ya hemos visto, tambi茅n calcula la longitud de cadenas y listas, demostrando su comportamiento polim贸rfico.

Ejemplo: El Operador + (Concatenaci贸n)

El operador +, cuando se utiliza con tuplas, realiza la operaci贸n de concatenaci贸n, creando una nueva tupla que contiene los elementos de las tuplas originales.

      
tupla1 = (10, 20)
tupla2 = (30, 40, 50)
tupla_combinada = tupla1 + tupla2
print(tupla_combinada) # Output: (10, 20, 30, 40, 50)
      
     

Al igual que con las cadenas y las listas, el operador + se comporta de manera diferente con las tuplas, lo que es un ejemplo de polimorfismo de operador.

Ejemplo: El Operador in (Pertenencia)

El operador in se utiliza para verificar si un elemento est谩 presente dentro de una tupla, funcionando de manera similar a c贸mo lo hace con cadenas y listas.

      
mi_tupla = ('manzana', 'banana', 'cereza')
print('banana' in mi_tupla) # Output: True
print('uva' in mi_tupla)    # Output: False

mi_lista = [1, 2, 3]
print(2 in mi_lista)       # Output: True
      
     

La consistencia en el uso del operador in a trav茅s de diferentes tipos de secuencia es un claro ejemplo de polimorfismo.

La Inmutabilidad de las Tuplas:

Es importante recordar que, al igual que las cadenas, las tuplas son inmutables. Las operaciones que parecen modificarlas (como la concatenaci贸n) en realidad crean una nueva tupla.

En el siguiente tema, exploraremos el polimorfismo en el contexto de los diccionarios en Python.

Dictionary

馃攽 Polimorfismo con Diccionarios (dict)

Los diccionarios en Python son colecciones de pares clave-valor. Aunque no son secuencias ordenadas como cadenas o tuplas, tambi茅n participan en el polimorfismo a trav茅s de funciones y m茅todos que operan sobre ellos de manera consistente.

Ejemplo: La Funci贸n len()

La funci贸n len(), cuando se aplica a un diccionario, devuelve el n煤mero de pares clave-valor (铆tems) que contiene el diccionario.

      
mi_diccionario = {"nombre": "Ana", "edad": 30, "ciudad": "Madrid"}
print(len(mi_diccionario)) # Output: 3
      
     

Una vez m谩s, la misma funci贸n len() se adapta al tipo de objeto, mostrando su comportamiento polim贸rfico.

Ejemplo: El Operador in (Pertenencia de Claves)

El operador in, cuando se utiliza con un diccionario, verifica si una clave espec铆fica existe en el diccionario.

      
mi_diccionario = {"a": 1, "b": 2, "c": 3}
print("b" in mi_diccionario) # Output: True
print(1 in mi_diccionario)   # Output: False (verifica claves, no valores)

mi_lista = [1, 2, 3]
print(2 in mi_lista)       # Output: True
      
     

Aunque la sem谩ntica de "pertenencia" es diferente (claves en diccionarios, elementos en listas), el operador in se utiliza de forma consistente, lo que puede considerarse una forma de polimorfismo a nivel de operador.

Ejemplo: M茅todos Comunes (Conceptualmente)

Los diccionarios tienen m茅todos como keys(), values() y items() que devuelven vistas (objetos iterables) de las claves, los valores y los pares clave-valor respectivamente. La idea de iterar sobre los "elementos" de una colecci贸n es com煤n a diferentes tipos, aunque la naturaleza de esos "elementos" var铆e.

      
mi_diccionario = {"nombre": "Carlos", "profesion": "Ingeniero"}
for clave in mi_diccionario.keys():
    print(clave)
# Output:
# nombre
# profesion

for valor in mi_diccionario.values():
    print(valor)
# Output:
# Carlos
# Ingeniero
      
     

La capacidad de iterar sobre los contenidos de un diccionario de manera similar a c贸mo se itera sobre los elementos de una lista o los caracteres de una cadena es un ejemplo de polimorfismo a nivel de comportamiento.

La Naturaleza de Mapeo de los Diccionarios:

Es crucial recordar que los diccionarios son mapeos, no secuencias ordenadas (hasta Python 3.7). Su comportamiento polim贸rfico se manifiesta en las operaciones que son significativas para su estructura de clave-valor.

En el siguiente tema, exploraremos el polimorfismo de clase.

Class Polymorphism

馃彚 Polimorfismo de Clase: Objetos con la Misma Interfaz, Diferentes Implementaciones

El polimorfismo de clase ocurre cuando diferentes clases definen m茅todos con el mismo nombre, pero estos m茅todos realizan acciones que son espec铆ficas para cada clase. Esto permite tratar objetos de diferentes clases de manera uniforme si comparten una interfaz com煤n (un conjunto de m茅todos con los mismos nombres).

Ejemplo: Figuras Geom茅tricas

Consideremos diferentes clases que representan figuras geom茅tricas, como un Circulo y un Cuadrado. Ambas figuras pueden tener un m茅todo para calcular su 谩rea, pero la forma en que se calcula el 谩rea es diferente para cada figura.

      
import math

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

    def calcular_area(self):
        return math.pi * self.radio**2

class Cuadrado:
    def __init__(self, lado):
        self.lado = lado

    def calcular_area(self):
        return self.lado**2

def mostrar_area(figura):
    print(f"El 谩rea de la figura es: {figura.calcular_area()}")

# Crear instancias de las clases
mi_circulo = Circulo(5)
mi_cuadrado = Cuadrado(4)

# Llamar a la misma funci贸n con diferentes objetos
mostrar_area(mi_circulo)  # Output: El 谩rea de la figura es: 78.53981633974483
mostrar_area(mi_cuadrado) # Output: El 谩rea de la figura es: 16.0
      
     

En este ejemplo:

  • Tanto la clase Circulo como la clase Cuadrado tienen un m茅todo llamado calcular_area().
  • La funci贸n mostrar_area() toma un objeto (que esperamos que tenga un m茅todo calcular_area()) y llama a ese m茅todo sin preocuparse por el tipo espec铆fico de la figura.
  • El resultado de calcular_area() es diferente para un c铆rculo y un cuadrado, lo que demuestra el polimorfismo de clase.

Beneficios del Polimorfismo de Clase:

  • Abstracci贸n: Permite trabajar con objetos a trav茅s de una interfaz com煤n, ocultando los detalles de implementaci贸n espec铆ficos de cada clase.
  • Flexibilidad: Facilita la adici贸n de nuevas clases que compartan la misma interfaz, sin necesidad de modificar el c贸digo que utiliza esa interfaz.
  • Mantenibilidad: El c贸digo se vuelve m谩s organizado y f谩cil de entender, ya que las responsabilidades se distribuyen entre las diferentes clases.

Duck Typing en Python:

Python implementa el polimorfismo a trav茅s de un concepto conocido como "duck typing". Si un objeto "camina como un pato y grazna como un pato", entonces se le trata como un pato. En otras palabras, lo que importa no es la clase del objeto, sino si implementa los m茅todos y atributos que se esperan.

En el ejemplo anterior, mientras los objetos mi_circulo y mi_cuadrado tengan un m茅todo calcular_area(), la funci贸n mostrar_area() podr谩 trabajar con ellos, independientemente de su tipo espec铆fico.

En el siguiente tema, veremos c贸mo la herencia potencia a煤n m谩s el polimorfismo de clase.

Inheritance Class Polymorphism

馃К Polimorfismo de Clase a Trav茅s de la Herencia: Un Comportamiento Com煤n en una Jerarqu铆a

Cuando una clase hereda de una clase padre, hereda sus m茅todos y atributos. El polimorfismo a trav茅s de la herencia se da cuando una clase hija **sobrescribe** (define un m茅todo con el mismo nombre que en la clase padre) un m茅todo heredado o implementa un m茅todo definido en una interfaz o clase abstracta padre. Esto permite que objetos de diferentes clases en la misma jerarqu铆a respondan al mismo m茅todo de manera espec铆fica a su tipo.

Ejemplo: Animales Hablando

Consideremos una clase padre llamada Animal con un m茅todo hablar(). Luego, creamos clases hijas como Perro y Gato que heredan de Animal y sobrescriben el m茅todo hablar() para producir el sonido caracter铆stico de cada animal.

      
class Animal:
    def hablar(self):
        print("Sonido gen茅rico de animal")

class Perro(Animal):
    def hablar(self):
        print("¡Guau!")

class Gato(Animal):
    def hablar(self):
        print("¡Miau!")

def hacer_hablar(animal):
    animal.hablar()

# Crear instancias de las clases hijas
mi_perro = Perro()
mi_gato = Gato()

# Tratar objetos de diferentes clases de manera uniforme
hacer_hablar(mi_perro) # Output: ¡Guau!
hacer_hablar(mi_gato)  # Output: ¡Miau!

# Tambi茅n podemos tratar a los objetos como instancias de la clase padre
un_animal_perro = Perro()
un_animal_gato = Gato()

hacer_hablar(un_animal_perro) # Output: ¡Guau!
hacer_hablar(un_animal_gato)  # Output: ¡Miau!
      
     

En este ejemplo:

  • La clase Animal define una interfaz com煤n con el m茅todo hablar().
  • Las clases hijas Perro y Gato proporcionan implementaciones espec铆ficas del m茅todo hablar().
  • La funci贸n hacer_hablar() puede tomar como argumento cualquier objeto que sea una instancia de Animal o de cualquiera de sus clases hijas, y llamar谩 al m茅todo hablar() espec铆fico de ese objeto.

Beneficios del Polimorfismo con Herencia:

  • Reutilizaci贸n de c贸digo: La clase padre proporciona una base com煤n que las clases hijas pueden extender o modificar.
  • Extensibilidad: Se pueden a帽adir nuevas clases hijas que hereden de la clase padre y proporcionen su propia implementaci贸n de los m茅todos polim贸rficos, sin necesidad de modificar el c贸digo existente que utiliza la clase padre.
  • Mantenibilidad: Los cambios en el comportamiento espec铆fico de una clase se limitan a esa clase, lo que facilita el mantenimiento del c贸digo.

Uso de super() en el Polimorfismo:

En m茅todos sobrescritos, a menudo se utiliza la funci贸n super() para llamar a la implementaci贸n del m茅todo en la clase padre antes o despu茅s de a帽adir un comportamiento espec铆fico en la clase hija. Esto permite extender el comportamiento heredado en lugar de reemplazarlo por completo.

Con esto, hemos explorado c贸mo la herencia y el polimorfismo trabajan juntos para crear sistemas de objetos flexibles y extensibles en Python.




Publicar un comentario

0 Comentarios