Fundamentos de OOP en Python

Programación Orientada a Objetos

La programación orientada a objetos (OOP) es un paradigma de programación basado en el concepto de "objetos", que pueden contener datos y código. Los datos tienen forma de campos (a menudo conocidos como atributos o propiedades), y el código tiene forma de procedimientos (a menudo conocidos como métodos).

Encapsulación

La encapsulación es uno de los conceptos fundamentales de la programación orientada a objetos, que ayuda a proteger los datos y métodos de un objeto del acceso y modificación no autorizados. Es una forma de lograr la abstracción de datos, lo que significa que los detalles de implementación de un objeto se ocultan del mundo exterior y solo se expone la información esencial.

En Python, la encapsulación se puede lograr mediante el uso de modificadores de acceso. Los modificadores de acceso son palabras clave que definen la accesibilidad de los atributos y métodos en una clase. Los tres modificadores de acceso disponibles en Python son público, privado y protegido. Sin embargo, Python no tiene una forma explícita de definir modificadores de acceso como otros lenguajes de programación como Java y C++. En su lugar, utiliza una convención de usar prefijos de guion bajo para indicar el nivel de acceso.

En el ejemplo de código proporcionado, la clase MyClass tiene dos atributos, _protected_var y __private_var. El _protected_var está marcado como protegido mediante el uso de un prefijo de guion bajo simple. Esto significa que el atributo puede ser accedido dentro de la clase y sus subclases, pero no fuera de la clase. El __private_var está marcado como privado mediante el uso de un prefijo de dos guiones bajos. Esto significa que el atributo solo puede ser accedido dentro de la clase y no fuera de la clase, ni siquiera en sus subclases.

Cuando creamos un objeto de la clase MyClass, podemos acceder al atributo _protected_var usando el nombre del objeto con un prefijo de guion bajo simple. Sin embargo, no podemos acceder al atributo __private_var usando el nombre del objeto, ya que está oculto del mundo exterior. Si intentamos acceder al atributo __private_var, obtendremos un AttributeError como se muestra en el código.

En resumen, la encapsulación es un concepto importante en la programación orientada a objetos que ayuda a proteger los detalles de implementación de un objeto. En Python, podemos lograr la encapsulación utilizando modificadores de acceso y usando prefijos de guion bajo para indicar el nivel de acceso.

# Define una clase llamada MyClass
class MyClass:

    # Método constructor que inicializa el objeto de la clase
    def __init__(self):

        # Define una variable protegida con un valor inicial de 10
        # El nombre de la variable comienza con un guion bajo simple, lo que indica acceso protegido
        self._protected_var = 10

        # Define una variable privada con un valor inicial de 20
        # El nombre de la variable comienza con dos guiones bajos, lo que indica acceso privado
        self.__private_var = 20

# Crea un objeto de la clase MyClass
obj = MyClass()

# Accede a la variable protegida usando el nombre del objeto y muestra su valor
# La variable protegida se puede acceder fuera de la clase, pero
# está destinada a ser utilizada dentro de la clase o sus subclases
print(obj._protected_var)   # output: 10

# Intenta acceder a la variable privada usando el nombre del objeto y muestra su valor
# La variable privada no se puede acceder fuera de la clase, ni siquiera por sus subclases
# Esto generará un AttributeError porque la variable no es accesible fuera de la clase
print(obj.__private_var)    # AttributeError: 'MyClass' object has no attribute '__private_var'

Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Cómo se indica una variable protegida en Python?
A. Prefijo de dos guiones bajos: **variable
B. Prefijo de guion bajo simple: _variable
C. No se necesita guion bajo
D. Prefijo de tres guiones bajos: _**variable

Herencia

La herencia promueve la reutilización de código y permite crear una jerarquía de clases que comparten atributos y métodos comunes. Ayuda a crear código limpio y organizado al mantener la funcionalidad relacionada en un solo lugar y promover el concepto de modularidad. La clase base de la que se deriva una nueva clase también se conoce como clase padre, y la nueva clase se conoce como clase hija o subclase.

En el código, definimos una clase llamada Animal que tiene un método constructor que inicializa el objeto de la clase con un atributo name y un método llamado speak. El método speak se define en la clase Animal pero no tiene cuerpo.

Luego definimos dos subclases llamadas Dog y Cat que heredan de la clase Animal. Estas subclases anulan el método speak de la clase Animal.

Creamos un objeto Dog con un atributo name “Rover” y un objeto Cat con un atributo name “Whiskers”. Llamamos al método speak del objeto Dog usando dog.speak(), e imprime “Woof!” porque el método speak de la clase Dog anula el método speak de la clase Animal. De manera similar, llamamos al método speak del objeto Cat usando cat.speak(), e imprime “Meow!” porque el método speak de la clase Cat anula el método speak de la clase Animal.

# Define una clase llamada Animal
class Animal:

    # Método constructor que inicializa el objeto de la clase con un atributo name
    def __init__(self, name):
        self.name = name

    # Método que se define en la clase Animal pero no tiene cuerpo
    # Este método será anulado en las subclases de Animal
    def speak(self):
        print("")

# Define una subclase llamada Dog que hereda de la clase Animal
class Dog(Animal):

    # Anula el método speak de la clase Animal
    def speak(self):
        print("Woof!")

# Define una subclase llamada Cat que hereda de la clase Animal
class Cat(Animal):

    # Anula el método speak de la clase Animal
    def speak(self):
        print("Meow!")

# Crea un objeto Dog con un atributo name "Rover"
dog = Dog("Rover")

# Crea un objeto Cat con un atributo name "Whiskers"
cat = Cat("Whiskers")

# Llama al método speak de la clase Dog e imprime la salida
# El método speak de la clase Dog anula el método speak de la clase Animal
# Por lo tanto, cuando llamamos al método speak del objeto Dog, imprimirá "Woof!"
dog.speak()   # output: Woof!

# Llama al método speak de la clase Cat e imprime la salida
# El método speak de la clase Cat anula el método speak de la clase Animal
# Por lo tanto, cuando llamamos al método speak del objeto Cat, imprimirá "Meow!"
cat.speak()   # output: Meow!

Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué es la herencia en OOP de Python?
A. Un mecanismo donde una clase puede heredar atributos y métodos de otra clase
B. Una forma de copiar objetos
C. Un método para eliminar clases
D. Una función incorporada

Polimorfismo

El polimorfismo es un concepto importante en la programación orientada a objetos que le permite escribir código que puede funcionar con objetos de diferentes clases de manera uniforme. En Python, el polimorfismo se logra mediante la anulación de métodos o la sobrecarga de métodos.

La anulación de métodos (method overriding) es cuando una subclase proporciona su propia implementación de un método que ya está definido en su clase padre. Esto permite que la subclase modifique el comportamiento del método sin cambiar su nombre o firma.

La sobrecarga de métodos (method overloading) es cuando múltiples métodos tienen el mismo nombre pero diferentes parámetros. Python no admite la sobrecarga de métodos directamente, pero se puede lograr utilizando argumentos predeterminados o argumentos de longitud variable.

El polimorfismo facilita la escritura de código flexible y reutilizable. Permite escribir código que puede funcionar con diferentes objetos sin necesidad de conocer sus tipos específicos.

#La clase Shape se define con un método area abstracto, que está destinado a ser anulado por las subclases.
class Shape:
    def area(self):
        pass

class Rectangle(Shape):
    # La clase Rectangle se define con un método __init__ que inicializa
    # las variables de instancia width y height.
    # También define un método area que calcula y devuelve
    # el área de un rectángulo usando las variables de instancia width y height.
    def __init__(self, width, height):
        self.width = width  # Inicializa la variable de instancia width
        self.height = height  # Inicializa la variable de instancia height

    def area(self):
        return self.width * self.height  # Devuelve el área del rectángulo


 # La clase Circle se define con un método __init__
 # que inicializa una variable de instancia radius.
 # También define un método area que calcula y
 # devuelve el área de un círculo usando la variable de instancia radius.
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius  # Inicializa la variable de instancia radius

    def area(self):
        return 3.14 * self.radius ** 2  # Devuelve el área del círculo usando pi * r^2

# La lista shapes se crea con un objeto Rectangle y un objeto Circle. El bucle for
# itera sobre cada objeto en la lista y llama al método area de cada objeto
# La salida será el área del rectángulo (20) y el área del círculo (153.86).
shapes = [Rectangle(4, 5), Circle(7)]  # Crea una lista de objetos Shape
for shape in shapes:
    print(shape.area())  # Muestra el área de cada objeto Shape

Quiz

Inicia sesión para responder este quiz y rastrear tu progreso de aprendizaje

¿Qué es el polimorfismo en OOP de Python?
A. Crear múltiples clases con el mismo nombre
B. Ocultar detalles de implementación
C. La capacidad de usar objetos de diferentes clases de manera uniforme a través de una interfaz común
D. Copiar objetos

Abstracción

La abstracción es un concepto importante en la programación orientada a objetos (OOP) porque le permite centrarse en las características esenciales de un objeto o sistema mientras ignora los detalles que no son relevantes para el contexto actual. Al reducir la complejidad y ocultar detalles innecesarios, la abstracción puede hacer que el código sea más modular, más fácil de leer y más fácil de mantener.

En Python, la abstracción se puede lograr mediante el uso de clases abstractas o interfaces. Una clase abstracta es una clase que no se puede instanciar directamente, sino que está destinada a ser subclaseada por otras clases. A menudo incluye métodos abstractos que no tienen implementación, pero proporcionan una plantilla de cómo debe implementarse la subclase. Esto permite al programador definir una interfaz común para un grupo de clases relacionadas, al tiempo que permite que cada clase tenga su propio comportamiento específico.

Una interfaz, por otro lado, es una colección de firmas de métodos que una clase debe implementar para ser considerada “compatible” con la interfaz. Las interfaces a menudo se utilizan para definir un conjunto común de métodos que varias clases pueden implementar, lo que les permite usarse indistintamente en ciertos contextos.

Python no tiene soporte incorporado para clases abstractas o interfaces, pero se pueden implementar utilizando el módulo abc (abstract base class). Este módulo proporciona la clase ABC y el decorador abstractmethod, que se pueden usar para definir clases y métodos abstractos.

En general, la abstracción es una herramienta poderosa para gestionar la complejidad y mejorar la calidad del código en la programación orientada a objetos, y Python proporciona una variedad de opciones para lograr la abstracción en su código.

# Importa el módulo abc para definir clases y métodos abstractos
from abc import ABC, abstractmethod

# Define una clase abstracta llamada Shape que tiene un método abstracto llamado area
class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

# Define una clase Rectangle que hereda de Shape
class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    # Implementa el método area para Rectángulos
    def area(self):
        return self.width * self.height

# Define una clase Circle que también hereda de Shape
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    # Implementa el método area para Círculos
    def area(self):
        return 3.14 * self.radius ** 2

# Crea una lista de formas que incluye tanto Rectángulos como Círculos
shapes = [Rectangle(4, 5), Circle(7)]

# Itera sobre cada forma en la lista e imprime su área
for shape in shapes:
    print(shape.area())

Estos son algunos de los principios básicos de OOP en Python. Esta página está actualmente en progreso y pronto habrá ejemplos y explicaciones más detallados.

Enlaces relevantes