Fundamentos

Tutorial Fundamentos: Concepto de clases y objetos

Python y la POO: Aprende a definir clases, crear objetos y aplicar encapsulación con la última versión de Python. Ideal para principiantes.

Aprende Fundamentos GRATIS y certifícate

Definición de clase: atributos y métodos

En la programación orientada a objetos (POO), una clase es una estructura que permite definir un tipo de dato abstracto que combina datos y comportamientos. Una clase actúa como un molde a partir del cual se pueden crear instancias concretas denominadas objetos.

Los atributos son las características o propiedades que describen el estado de los objetos. Representan los datos que cada instancia de la clase mantendrá. Por ejemplo, en una clase Coche, los atributos podrían ser marca, modelo y color. Estos atributos se definen dentro de la clase y cada objeto tendrá sus propios valores para ellos.

Los métodos son las acciones o comportamientos que los objetos pueden realizar. Son funciones definidas dentro de la clase que operan sobre los atributos o realizan operaciones específicas. Siguiendo el ejemplo anterior, la clase Coche podría tener métodos como acelerar(), frenar() o girar(angulo).

A continuación, se muestra cómo definir una clase en Python con atributos y métodos:

class Coche:
    def __init__(self, marca, modelo, color):
        self.marca = marca   # Atributo marca
        self.modelo = modelo # Atributo modelo
        self.color = color   # Atributo color

    def acelerar(self):
        print(f"El {self.marca} {self.modelo} está acelerando.")  # Método acelerar

    def frenar(self):
        print(f"El {self.marca} {self.modelo} está frenando.")    # Método frenar

En este ejemplo, la clase Coche tiene un método especial __init__, conocido como constructor, que se utiliza para inicializar los atributos cuando se crea un nuevo objeto. Los parámetros marca, modelo y color se asignan a los atributos del objeto utilizando self, que hace referencia a la instancia actual de la clase.

Los métodos de instancia, como acelerar y frenar, permiten que los objetos realizados acciones utilizando sus propios atributos. Al invocar estos métodos, el objeto utiliza su estado interno para ejecutar las operaciones definidas.

Es posible definir atributos de clase, que son compartidos por todas las instancias. Por ejemplo:

class Coche:
    numero_ruedas = 4  # Atributo de clase compartido por todos los coches

    def __init__(self, marca, modelo, color):
        self.marca = marca
        self.modelo = modelo
        self.color = color

Aquí, numero_ruedas es un atributo común a todos los objetos de la clase Coche. Esto es útil para definir propiedades que son iguales para todas las instancias.

Los métodos de clase y los métodos estáticos son otros tipos de métodos que no operan sobre instancias individuales. Un método de clase recibe la clase como primer argumento y se define con el decorador @classmethod, mientras que un método estático no recibe ningún argumento especial y se define con @staticmethod:

class Coche:
    numero_ruedas = 4

    @classmethod
    def get_numero_ruedas(cls):
        return cls.numero_ruedas  # Método de clase que accede al atributo de clase

    @staticmethod
    def velocidad_maxima():
        return 240  # Método estático que devuelve un valor fijo

Los atributos y métodos pueden tener diferentes niveles de acceso. Para indicar que un atributo o método es privado, se utiliza una convención de nomenclatura con dos guiones bajos (__). Por ejemplo:

class CuentaBancaria:
    def __init__(self, titular, saldo):
        self.titular = titular
        self.__saldo = saldo  # Atributo privado

    def depositar(self, cantidad):
        self.__saldo += cantidad  # Método que modifica un atributo privado

    def __actualizar_saldo(self):
        pass  # Método privado

    def obtener_saldo(self):
        return self.__saldo  # Método público que accede a un atributo privado

En este caso, __saldo es un atributo privado y __actualizar_saldo() es un método privado. Esto implementa el principio de encapsulación, protegiendo el estado interno de la clase y controlando cómo se accede o modifica.

La definición clara y estructurada de clases, atributos y métodos es esencial para crear programas organizados y facilitate el mantenimiento del código. Permite modelar entidades complejas del mundo real de manera lógica y coherente, aprovechando las ventajas que ofrece la programación orientada a objetos.

Instanciación de objetos: creación y uso

Tras definir una clase, el siguiente paso es crear objetos concretos que representen instancias de esa clase. La instanciación es el proceso mediante el cual se crea un nuevo objeto utilizando el constructor de una clase, lo que permite trabajar con datos y comportamientos específicos.

Para instanciar un objeto en Python, se utiliza la sintaxis nombre_de_clase(parámetros). Esto llama al método __init__ de la clase y devuelve una nueva instancia. Por ejemplo, si disponemos de la clase Coche:

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

    def acelerar(self):
        print(f"El {self.marca} {self.modelo} está acelerando.")

    def frenar(self):
        print(f"El {self.marca} {self.modelo} está frenando.")

Podemos crear una instancia de Coche de la siguiente manera:

mi_coche = Coche("Renault", "Clio", "Blanco")

Aquí, mi_coche es un objeto de la clase Coche con los atributos marca, modelo y color inicializados. Podemos acceder a sus atributos utilizando la notación de punto:

print(mi_coche.marca)   # Imprime: Renault
print(mi_coche.modelo)  # Imprime: Clio
print(mi_coche.color)   # Imprime: Blanco

También es posible invocar sus métodos de la misma manera:

mi_coche.acelerar()  # Salida: El Renault Clio está acelerando.
mi_coche.frenar()    # Salida: El Renault Clio está frenando.

La instanciación permite crear múltiples objetos con diferentes estados. Por ejemplo:

coche_de_ana = Coche("Peugeot", "208", "Rojo")
coche_de_luis = Coche("Volkswagen", "Golf", "Negro")

print(coche_de_ana.marca)  # Imprime: Peugeot
print(coche_de_luis.color) # Imprime: Negro

Cada instancia de Coche mantiene sus propios valores de atributos, lo que refleja la independencia entre los objetos creados a partir de la misma clase.

Es posible modificar los atributos de un objeto después de su creación:

mi_coche.color = "Azul"
print(mi_coche.color)  # Imprime: Azul

Sin embargo, es recomendable controlar el acceso a los atributos mediante métodos específicos para mantener la integridad de los datos, siguiendo los principios de la encapsulación.

La creación y uso de objetos también facilita la organización y manipulación de conjuntos de datos. Por ejemplo, podemos almacenar objetos en una lista:

flota = [mi_coche, coche_de_ana, coche_de_luis]

for coche in flota:
    coche.acelerar()

Este código recorrerá cada objeto en la lista flota y llamará al método acelerar() de cada uno, demostrando cómo los objetos pueden ser manejados de forma colectiva.

Además, podemos pasar objetos como argumentos a funciones o métodos:

def mostrar_informacion(coche):
    print(f"Marca: {coche.marca}, Modelo: {coche.modelo}, Color: {coche.color}")

mostrar_informacion(mi_coche)

La instanciación de objetos es esencial para aprovechar las ventajas de la programación orientada a objetos, permitiendo modelar entidades reales y crear aplicaciones más modulables y mantenibles. Al utilizar objetos, se puede encapsular la complejidad y trabajar con abstracciones que representan conceptos del mundo real.

Es importante entender que mientras la clase define la estructura y comportamiento general, los objetos son instancias concretas con datos específicos. Esto permite crear programas flexibles y adaptables a diferentes contextos y necesidades.

Diferencia entre clases y objetos en la práctica

En la programación orientada a objetos, es fundamental entender la distinción entre una clase y un objeto para aplicar correctamente los conceptos y diseñar programas eficientes. Aunque las clases y los objetos están estrechamente relacionados, cumplen roles diferentes en la estructura del código y su ejecución.

Una clase es una plantilla o modelo que define las propiedades y comportamientos que tendrán sus objetos. Es una abstracción que encapsula datos y funciones, sin ocupar espacio en memoria hasta que se crean instancias de ella. Por otro lado, un objeto es una instancia concreta de una clase, que ocupa espacio en memoria y posee valores específicos en sus atributos.

Para ilustrar la diferencia en la práctica, consideremos el proceso de desarrollo de una aplicación de gestión de biblioteca. Se puede definir una clase Libro que describa las características generales de los libros:

class Libro:
    def __init__(self, titulo, autor, isbn):
        self.titulo = titulo
        self.autor = autor
        self.isbn = isbn
    
    def mostrar_informacion(self):
        print(f"'{self.titulo}' por {self.autor} (ISBN: {self.isbn})")

La clase Libro actúa como un molde que especifica qué atributos (titulo, autor, isbn) y métodos (mostrar_informacion) tendrán los libros. No representa ningún libro en particular hasta que se crean objetos a partir de ella.

Al crear objetos de la clase Libro, se generan instancias únicas con datos específicos:

libro1 = Libro("Cien años de soledad", "Gabriel García Márquez", "978-0307474728")
libro2 = Libro("Don Quijote de la Mancha", "Miguel de Cervantes", "978-8491050294")

Aquí, libro1 y libro2 son objetos diferentes, cada uno con sus propios valores en los atributos. Aunque comparten la misma estructura definida por la clase Libro, son entidades independientes en memoria.

En la práctica, la clase es utilizada por los desarrolladores para definir la estructura y el comportamiento común de un conjunto de objetos. Los objetos, en cambio, son manipulados en tiempo de ejecución, almacenan los datos y permiten la interacción con el usuario o con otros componentes del programa.

Es importante destacar que las clases pueden contener lógica compleja, métodos estáticos y atributos de clase, pero no representan elementos activos hasta que se instancian. Los objetos son los que interactúan durante la ejecución, permitiendo que el programa realice tareas concretas.

Otro ejemplo práctico es en el desarrollo de un juego. Se puede tener una clase Personaje que defina propiedades comunes a todos los personajes:

class Personaje:
    num_personajes = 0  # Atributo de clase

    def __init__(self, nombre, salud):
        self.nombre = nombre
        self.salud = salud
        Personaje.num_personajes += 1  # Acceso a atributo de clase

    def atacar(self, otro_personaje):
        print(f"{self.nombre} ataca a {otro_personaje.nombre}.")

    @classmethod
    def mostrar_total_personajes(cls):
        print(f"Total de personajes: {cls.num_personajes}")

La clase Personaje define la estructura para todos los personajes del juego, pero no existe ningún personaje específico hasta que se crean objetos:

hero = Personaje("Aragorn", 100)
villano = Personaje("Sauron", 150)

Los objetos hero y villano representan entidades individuales en el juego, con estados particulares que pueden cambiar a lo largo de la ejecución. El método mostrar_total_personajes es un ejemplo de cómo las clases pueden mantener y proporcionar información general sin referirse a una instancia concreta.

En términos prácticos, se trabaja con clases cuando se diseña y estructura el código, definiendo las relaciones y comportamientos genéricos. Se interactúa con objetos cuando se ejecuta el programa, manipulando datos específicos y realizando operaciones concretas.

Algunas diferencias clave en la práctica son:

  • Definición vs. instanciación: Las clases se definen una sola vez en el código, mientras que los objetos se pueden instanciar múltiples veces durante la ejecución.
  • Espacio en memoria: Las clases no ocupan espacio significativo en memoria hasta que se crean objetos; los objetos sí ocupan espacio, ya que almacenan datos.
  • Métodos y atributos: Las clases contienen la definición de métodos y atributos, pero los objetos tienen los valores concretos de esos atributos y ejecutan los métodos con su propio estado.
  • Uso: Se accede a los atributos y métodos de una clase a través de los objetos o, en el caso de atributos y métodos de clase, directamente desde la clase.

Por ejemplo, para acceder a un método de instancia:

hero.atacar(villano)  # El objeto hero realiza la acción atacar

Para acceder a un método de clase:

Personaje.mostrar_total_personajes()  # Se accede al método desde la clase

Entender esta distinción es esencial para aprovechar al máximo las capacidades de la programación orientada a objetos. Permite separar la lógica general (clases) de los datos específicos (objetos), facilitando la reutilización de código y el mantenimiento de aplicaciones complejas.

En resumen, mientras que la clase es una abstracción que describe qué es y qué puede hacer un objeto, el objeto es la realización concreta de esa descripción, con la que se interactúa directamente en el programa. Esta diferencia es fundamental para el diseño de software modular y escalable.

Ejemplos prácticos: modelado de entidades del mundo real

Para comprender cómo aplicar la programación orientada a objetos en problemas reales, es útil modelar entidades del mundo real como clases y objetos. A continuación, se presentan ejemplos prácticos que demuestran cómo traducir conceptos cotidianos en código.

Ejemplo 1: Modelo de un estudiante

Supongamos que queremos crear un sistema para gestionar la información de estudiantes en una universidad. Podemos definir una clase Estudiante con atributos y métodos relevantes.

class Estudiante:
    def __init__(self, nombre, edad, carrera):
        self.nombre = nombre        # Atributo nombre
        self.edad = edad            # Atributo edad
        self.carrera = carrera      # Atributo carrera
        self.notas = []             # Atributo notas inicializado como lista vacía

    def agregar_nota(self, nota):
        self.notas.append(nota)     # Método para agregar una nota

    def calcular_promedio(self):
        if self.notas:
            return sum(self.notas) / len(self.notas)  # Método para calcular el promedio
        else:
            return 0

    def mostrar_informacion(self):
        print(f"Nombre: {self.nombre}")
        print(f"Edad: {self.edad}")
        print(f"Carrera: {self.carrera}")
        print(f"Promedio: {self.calcular_promedio():.2f}")

En este ejemplo, la clase Estudiante modela la entidad real de un estudiante. Los atributos nombre, edad y carrera almacenan la información personal, mientras que notas es una lista que guarda las calificaciones. Los métodos agregar_nota, calcular_promedio y mostrar_informacion permiten manipular y mostrar los datos del estudiante.

Podemos crear instancias de Estudiante y utilizarlas en nuestro programa:

estudiante1 = Estudiante("María García", 20, "Ingeniería Informática")
estudiante1.agregar_nota(85)
estudiante1.agregar_nota(90)
estudiante1.agregar_nota(78)

estudiante1.mostrar_informacion()

La salida será:

Nombre: María García
Edad: 20
Carrera: Ingeniería Informática
Promedio: 84.33

Ejemplo 2: Sistema de gestión de productos

Consideremos ahora el modelado de productos en una tienda. Podemos definir una clase Producto que represente los artículos disponibles.

class Producto:
    def __init__(self, codigo, nombre, precio, stock):
        self.codigo = codigo      # Atributo código de producto
        self.nombre = nombre      # Atributo nombre del producto
        self.precio = precio      # Atributo precio
        self.stock = stock        # Atributo stock disponible

    def vender(self, cantidad):
        if self.stock >= cantidad:
            self.stock -= cantidad
            print(f"Se han vendido {cantidad} unidades de {self.nombre}.")
        else:
            print(f"No hay suficiente stock de {self.nombre}.")

    def reponer(self, cantidad):
        self.stock += cantidad
        print(f"Se han agregado {cantidad} unidades al stock de {self.nombre}.")

    def mostrar_informacion(self):
        print(f"Código: {self.codigo}")
        print(f"Producto: {self.nombre}")
        print(f"Precio: {self.precio}€")
        print(f"Stock: {self.stock}")

La clase Producto representa un artículo con sus atributos y métodos para gestionar el inventario. Podemos instanciar y utilizar esta clase en un sistema de ventas:

producto1 = Producto("P001", "Camiseta", 19.99, 50)
producto1.mostrar_informacion()

producto1.vender(5)
producto1.mostrar_informacion()

producto1.reponer(10)
producto1.mostrar_informacion()

Este código gestionará las operaciones sobre el producto, actualizando el stock y mostrando la información actualizada.

Ejemplo 3: Gestión de una cuenta bancaria

Otro ejemplo es el modelado de una cuenta bancaria. Definimos una clase CuentaBancaria que permite operaciones básicas.

class CuentaBancaria:
    def __init__(self, numero_cuenta, titular, saldo=0):
        self.numero_cuenta = numero_cuenta  # Atributo número de cuenta
        self.titular = titular              # Atributo titular de la cuenta
        self.__saldo = saldo                # Atributo privado saldo

    def depositar(self, cantidad):
        self.__saldo += cantidad
        print(f"Se han depositado {cantidad}€. Nuevo saldo: {self.__saldo}€.")

    def retirar(self, cantidad):
        if self.__saldo >= cantidad:
            self.__saldo -= cantidad
            print(f"Se han retirado {cantidad}€. Nuevo saldo: {self.__saldo}€.")
        else:
            print("Saldo insuficiente.")

    def consultar_saldo(self):
        print(f"El saldo actual es: {self.__saldo}€.")

En esta clase, el atributo __saldo es privado para proteger la información financiera. Los métodos permiten realizar depósitos, retiros y consultar el saldo. Ejemplo de uso:

cuenta1 = CuentaBancaria("ES1234567890", "Juan Pérez", 1000)
cuenta1.consultar_saldo()

cuenta1.depositar(500)
cuenta1.retirar(200)
cuenta1.consultar_saldo()

Este código simula operaciones bancarias básicas, respetando la encapsulación de datos sensibles.

Integración de objetos

Además de crear clases individuales, es común que los objetos interactúen entre sí. Por ejemplo, un sistema de gestión de pedidos donde un Cliente realiza un Pedido que contiene varios Productos.

Definimos una clase Cliente y una clase Pedido:

class Cliente:
    def __init__(self, nombre, direccion, telefono):
        self.nombre = nombre        # Atributo nombre
        self.direccion = direccion  # Atributo dirección
        self.telefono = telefono    # Atributo teléfono

    def mostrar_informacion(self):
        print(f"Cliente: {self.nombre}")
        print(f"Dirección: {self.direccion}")
        print(f"Teléfono: {self.telefono}")
class Pedido:
    def __init__(self, cliente):
        self.cliente = cliente         # Atributo cliente asociado al pedido
        self.productos = []            # Lista de productos en el pedido

    def agregar_producto(self, producto, cantidad):
        self.productos.append((producto, cantidad))  # Añade una tupla de producto y cantidad

    def mostrar_pedido(self):
        self.cliente.mostrar_informacion()
        print("Productos del pedido:")
        total = 0
        for producto, cantidad in self.productos:
            subtotal = producto.precio * cantidad
            print(f"- {producto.nombre} x {cantidad} unidades: {subtotal}€")
            total += subtotal
        print(f"Total a pagar: {total}€")

Con estas clases, podemos simular la realización de un pedido:

cliente1 = Cliente("Ana López", "Calle Mayor 10", "600123456")
producto1 = Producto("P001", "Camiseta", 19.99, 50)
producto2 = Producto("P002", "Pantalón", 39.99, 30)

pedido1 = Pedido(cliente1)
pedido1.agregar_producto(producto1, 2)
pedido1.agregar_producto(producto2, 1)

pedido1.mostrar_pedido()

Esta interacción entre objetos refleja situaciones reales donde diferentes entidades colaboran. El pedido incluye productos y está asociado a un cliente, demostrando la capacidad de los objetos para modelar sistemas complejos.

Para seguir leyendo hazte Plus

¿Ya eres Plus? Accede a la app

Plan mensual

19.00 € /mes

Precio normal mensual: 19 €
47 % DE DESCUENTO

Plan anual

10.00 € /mes

Ahorras 108 € al año
Precio normal anual: 120 €
Aprende Fundamentos GRATIS online

Todas las lecciones de Fundamentos

Accede a todas las lecciones de Fundamentos y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Accede GRATIS a Fundamentos y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender el concepto de clases, atributos y métodos.
  • Aprender la sintaxis de definición de clases en Python.
  • Captar la distinción entre clases y objetos.
  • Entender la encapsulación y el acceso a atributos privados.
  • Crear y manipular instancias de objetos.
  • Aplicar POO en modelado de entidades del mundo real.