Clases y objetos en Python: modela problemas del mundo real
Aprende qué son las clases y los objetos en Python y cómo utilizarlos para representar problemas del mundo real. Comprende los atributos, métodos, __init__, self y las diferencias entre clases e instancias …
Contenido de la guía ⌄
- Aprender a programar también es aprender a representar el mundo
- Los datos por sí solos no siempre cuentan toda la historia
- ¿Las clases reemplazan los diccionarios y las funciones?
- Clase y objeto: el modelo y sus instancias
- Imaginemos dos libros diferentes
- ¿Cómo se representan las clases y los objetos en Python?
- Una misma clase, múltiples objetos
- ¿Qué acabamos de crear?
- ¿Qué resultados obtenemos?
- __init__ y self: cómo recibe sus datos cada objeto
- ¿Qué hace __init__?
- ¿Y qué significa self?
- ¿Qué ocurre al crear un libro?
- Los atributos: la información que pertenece a cada objeto
- ¿Por qué escribimos self.titulo = titulo?
- ¿Todos los objetos comparten los mismos valores?
- Los métodos: cuando un objeto puede realizar acciones
- Los métodos también pueden contener reglas
- ¿Qué ocurre cuando prestamos y devolvemos un libro?
- El objeto mantiene su estado
Aprender a programar también es aprender a representar el mundo
Imagina que necesitas desarrollar un programa para organizar una biblioteca, administrar productos de una tienda o registrar estudiantes de una institución.
Aunque estos problemas parecen diferentes, tienen algo en común: todos involucran entidades con características propias y acciones que pueden realizarse sobre ellas.
Un libro tiene un título, un autor y un estado de disponibilidad. Un producto tiene un precio y una cantidad disponible. Un estudiante tiene un nombre y puede matricularse en un curso.
¿Cómo podemos representar estas entidades dentro de un programa sin perder de vista sus características, sus comportamientos y sus relaciones?
Una de las herramientas que tenemos para resolver este problema es la programación orientada a objetos, también conocida como POO.
En esta guía comprenderemos cómo funcionan las clases y los objetos en Python, por qué pueden ser útiles y cómo identificar situaciones donde tiene sentido utilizarlos.
Nuestro objetivo no será construir una aplicación completa, sino desarrollar una forma de pensar: identificar las entidades de un problema y representar sus datos y comportamientos mediante código.
Utilizaremos un libro como ejemplo principal. A partir de él aprenderemos los conceptos fundamentales sin necesidad de conocer previamente programación orientada a objetos.
Los datos por sí solos no siempre cuentan toda la historia
En Python ya podemos representar información utilizando variables, listas, diccionarios y otras estructuras de datos.
Por ejemplo, para representar un libro podemos almacenar su título, su autor y si está disponible para préstamo.
Un libro puede tener un título, un autor, un año de publicación y un estado que indique si está disponible.
Consultar sus datos, registrarlo como prestado, devolverlo o comprobar si está disponible.
Podemos resolver estas operaciones con diccionarios y funciones independientes. De hecho, para programas pequeños esa solución puede ser suficiente.
Sin embargo, cuando necesitamos manejar muchos libros y aplicar las mismas reglas a cada uno, puede resultar conveniente organizar los datos y las operaciones relacionadas mediante una estructura común.
Una clase nos permite definir una forma de representar entidades de un mismo tipo, reuniendo atributos y métodos relacionados.
Después podemos crear objetos concretos que mantengan su propia información.
¿Las clases reemplazan los diccionarios y las funciones?
No. Python permite combinar distintos estilos de programación. Podemos utilizar funciones, diccionarios y clases dentro de un mismo programa.
La elección depende de lo que necesitamos representar y de qué estructura hace que el código sea más comprensible.
Clase y objeto: el modelo y sus instancias
Para comprender las clases y los objetos, pensemos en una editorial que publica libros.
Todos los libros comparten ciertas características generales. Tienen un título, un autor y pueden encontrarse disponibles o prestados.
Pero no todos los libros contienen la misma información. Cada ejemplar concreto puede tener un título diferente, un autor diferente y su propio estado de disponibilidad.
Una clase describe una estructura común para representar libros. Define qué atributos y métodos tendrán los objetos creados a partir de ella.
En Python utilizaremos
una clase llamada
Libro.
Un objeto es una instancia de una clase. Representa una entidad concreta que puede almacenar sus propios valores.
Podemos crear varios objetos de la clase Libro sin que todos tengan necesariamente los mismos datos.
Imaginemos dos libros diferentes
Establecemos que los objetos tendrán atributos como título y autor, además de comportamientos como prestar y devolver.
Un libro titulado "El principito", escrito por Antoine de Saint-Exupéry.
Otro libro titulado "Cien años de soledad", escrito por Gabriel García Márquez.
Ambos objetos pertenecen a la misma clase, pero cada uno conserva sus propios atributos de instancia.
La clase define cómo se organizan y comportan los objetos. Cada objeto es una instancia concreta creada a partir de esa clase.
La analogía de una clase como una plantilla es útil para comenzar, aunque una clase en Python también puede definir comportamientos, no solamente la estructura de los datos.
En la siguiente ilustración veremos cómo una sola clase puede dar lugar a dos objetos con características diferentes.
¿Cómo se representan las clases y los objetos en Python?
Hasta ahora hemos utilizado la idea de una clase como un modelo que permite representar entidades con características y comportamientos comunes.
Pero una clase no es solamente una idea teórica. En Python podemos definirla directamente mediante código.
Para ello utilizamos la palabra reservada
class,
seguida del nombre que queremos
asignar a la clase.
Es un objeto del lenguaje que permite definir atributos y métodos que estarán disponibles para sus instancias.
En nuestro ejemplo, la clase se llamará Libro.
Es una instancia concreta creada a partir de una clase.
Podemos crear tantos objetos Libro como necesitemos; cada uno tendrá su propia identidad.
Una misma clase, múltiples objetos
Imaginemos que una biblioteca necesita registrar cien libros.
No sería necesario escribir cien definiciones diferentes de la clase Libro.
Bastaría con definir la clase una vez y utilizarla para crear las distintas instancias.
Definir una clase y crear un objeto son dos operaciones diferentes.
La definición establece una estructura y un comportamiento. La instanciación crea un objeto concreto que puede utilizarse dentro del programa.
Ahora que entendemos la diferencia, vamos a escribir nuestra primera clase utilizando la sintaxis más sencilla posible.
# Nuestra primera clase en Python
class Libro:
pass
# Creamos dos objetos de la misma clase
libro_1 = Libro()
libro_2 = Libro()
# Comprobamos sus características
print("Clase:", type(libro_1).__name__)
print(
"¿Es un objeto de la clase Libro?",
isinstance(libro_1, Libro)
)
print(
"¿Los dos nombres hacen referencia al mismo objeto?",
libro_1 is libro_2
)
¿Qué acabamos de crear?
Nuestro primer ejemplo contiene una clase llamada Libro y dos objetos creados a partir de esa misma clase.
Aunque todavía no hemos definido atributos ni métodos propios, el programa ya nos permite observar varias características fundamentales de la programación orientada a objetos.
La palabra class indica que estamos definiendo una clase.
Libro es su nombre. Por convención, los nombres de las clases en Python utilizan palabras con inicial mayúscula.
Es una instrucción que no realiza ninguna acción.
La utilizamos porque Python requiere un bloque de código después de definir la clase.
Más adelante reemplazaremos esta instrucción por código que describa nuestros libros.
Al llamar a Libro(), Python crea una instancia de nuestra clase.
Guardamos la referencia a ese objeto en la variable libro_1.
Creamos una segunda instancia.
Aunque pertenece a la misma clase, es un objeto diferente del que creamos anteriormente.
¿Qué resultados obtenemos?
type() permite consultar el tipo del objeto. __name__ devuelve el nombre de ese tipo.
isinstance() confirma que libro_1 es una instancia de la clase Libro.
El operador is comprueba si dos referencias identifican exactamente el mismo objeto.
En este caso devuelve False porque creamos dos instancias diferentes.
Dos objetos pueden pertenecer a la misma clase sin ser el mismo objeto.
Más adelante veremos que también pueden conservar valores diferentes en sus atributos de instancia.
Nuestra clase todavía es muy sencilla. Los objetos existen, pero aún no almacenan el título ni el autor.
Para incorporar esos datos conoceremos dos elementos fundamentales de Python: __init__ y self.
__init__ y self: cómo recibe sus datos cada objeto
Imaginemos que ahora queremos registrar el título y el autor de cada libro.
Necesitamos una forma de indicar esos valores cuando creamos cada nueva instancia.
Python permite hacerlo mediante
un método especial llamado
__init__.
¿Qué hace __init__?
__init__ es un método especial que permite inicializar el estado de un objeto después de que ha sido creado.
Cuando llamamos a una clase como Libro(), Python puede ejecutar automáticamente su método __init__ para asignar los valores iniciales de esa instancia.
Aunque frecuentemente se llama constructor a __init__, su función específica es inicializar una instancia.
La creación del objeto y su inicialización son conceptos relacionados, pero no idénticos.
¿Y qué significa self?
self es el nombre que utilizamos por convención para referirnos a la instancia sobre la cual se está ejecutando un método.
Gracias a self, podemos guardar información dentro del objeto correspondiente.
Por ejemplo, cuando escribimos
self.titulo,
estamos accediendo al atributo
titulo de esa instancia.
Permite establecer los valores iniciales de sus atributos.
Podemos recibir información mediante parámetros, como titulo y autor.
Permite trabajar con los atributos y métodos del objeto que está ejecutando una operación.
Es una convención de nombre, no una palabra reservada del lenguaje.
¿Qué ocurre al crear un libro?
Llamamos a Libro() y proporcionamos los datos iniciales.
El método __init__ recibe la instancia mediante self y los argumentos que proporcionamos.
Las asignaciones self.titulo y self.autor almacenan valores asociados a esa instancia.
Con estos conceptos ya podemos mejorar nuestra clase Libro para que represente libros con información concreta.
# Clase Libro con atributos de instancia
class Libro:
def __init__(self, titulo, autor):
self.titulo = titulo
self.autor = autor
self.disponible = True
# Creamos un libro con datos concretos
libro = Libro(
"El principito",
"Antoine de Saint-Exupéry"
)
# Consultamos sus atributos
print("Título:", libro.titulo)
print("Autor:", libro.autor)
print("Disponible:", libro.disponible)
Título: El principito
Autor: Antoine de Saint-Exupéry
Disponible: True
Los atributos: la información que pertenece a cada objeto
En el ejemplo anterior mejoramos nuestra clase Libro para representar información de un libro concreto.
Ahora cada instancia puede almacenar su título, su autor y su estado de disponibilidad.
Estos valores se encuentran asociados a atributos de instancia.
Representa el nombre del libro.
Lo asignamos mediante
self.titulo.
Almacena el nombre de quien escribió la obra.
Lo asignamos mediante
self.autor.
Indica si el libro está disponible para préstamo.
Inicialmente utilizamos el valor booleano True.
¿Por qué escribimos self.titulo = titulo?
Aunque ambos nombres se parecen, representan cosas diferentes.
Es el nombre del parámetro que recibe __init__.
Contiene el valor proporcionado al crear la instancia.
Es un atributo asociado al objeto.
La asignación guarda allí el valor recibido.
Por eso, cuando posteriormente
escribimos
libro.titulo,
podemos consultar
el título guardado
en ese objeto.
¿Todos los objetos comparten los mismos valores?
No necesariamente. Cada instancia puede almacenar sus propios valores de atributos.
Si creamos dos objetos Libro, el título de uno puede ser diferente del título del otro.
También podemos modificar un atributo de un objeto sin modificar automáticamente el atributo correspondiente de otra instancia.
Dos instancias pueden utilizar la misma clase y los mismos métodos, pero conservar valores individuales en sus atributos.
Para comprobarlo, vamos a crear dos libros y modificar el estado de disponibilidad de uno de ellos.
# Dos objetos creados a partir de la misma clase
class Libro:
def __init__(self, titulo, autor):
self.titulo = titulo
self.autor = autor
self.disponible = True
# Primer objeto
libro_1 = Libro(
"El principito",
"Antoine de Saint-Exupéry"
)
# Segundo objeto
libro_2 = Libro(
"Cien años de soledad",
"Gabriel García Márquez"
)
# Cambiamos solo el segundo objeto
libro_2.disponible = False
# Consultamos sus datos
print("LIBRO 1")
print("Título:", libro_1.titulo)
print("Autor:", libro_1.autor)
print("Disponible:", libro_1.disponible)
print()
print("LIBRO 2")
print("Título:", libro_2.titulo)
print("Autor:", libro_2.autor)
print("Disponible:", libro_2.disponible)
print()
print(
"¿Son el mismo objeto?",
libro_1 is libro_2
)
LIBRO 1
Título: El principito
Autor: Antoine de Saint-Exupéry
Disponible: True
LIBRO 2
Título: Cien años de soledad
Autor: Gabriel García Márquez
Disponible: False
¿Son el mismo objeto? False
Los métodos: cuando un objeto puede realizar acciones
Hasta ahora hemos aprendido que un objeto puede almacenar información mediante atributos. Nuestro libro, por ejemplo, tiene un título, un autor y un estado de disponibilidad.
Pero representar una entidad no consiste solamente en describir sus características. También necesitamos expresar qué operaciones pueden realizarse sobre ella.
En programación orientada a objetos, estas operaciones suelen representarse mediante métodos.
El atributo disponible nos indica si un libro puede prestarse.
Ejemplo:
libro.disponible
El método prestar() puede cambiar el estado del libro cuando se realiza un préstamo.
Ejemplo:
libro.prestar()
Los métodos también pueden contener reglas
Imaginemos que alguien solicita un libro. Si está disponible, podemos prestarlo y cambiar su estado a False.
Sin embargo, si ya está prestado, la operación debería indicar que no fue posible realizar otro préstamo.
Por eso, un método no se limita a cambiar un valor: también puede comprobar condiciones y decidir qué debe ocurrir.
Los atributos representan información asociada a un objeto. Los métodos representan operaciones que podemos realizar utilizando ese objeto.
Vamos a incorporar dos métodos a nuestra clase Libro: prestar() y devolver().
class Libro:
def __init__(self, titulo, autor):
self.titulo = titulo
self.autor = autor
self.disponible = True
def prestar(self):
if not self.disponible:
return False
self.disponible = False
return True
def devolver(self):
if self.disponible:
return False
self.disponible = True
return True
# Creamos un libro
libro = Libro(
"El principito",
"Antoine de Saint-Exupéry"
)
print("Disponible al inicio:", libro.disponible)
print("Primer préstamo:", libro.prestar())
print("Disponible después:", libro.disponible)
print("Segundo préstamo:", libro.prestar())
print("Devolución:", libro.devolver())
print("Disponible al final:", libro.disponible)
Disponible al inicio: True
Primer préstamo: True
Disponible después: False
Segundo préstamo: False
Devolución: True
Disponible al final: True
¿Qué ocurre cuando prestamos y devolvemos un libro?
Nuestro ejemplo muestra que un objeto puede cambiar de estado durante la ejecución de un programa.
Al crearlo, el libro comienza con el atributo disponible establecido en True.
Cuando llamamos al método prestar(), comprobamos su disponibilidad y, si es posible realizar el préstamo, modificamos ese atributo.
El libro está disponible. Todavía no hemos realizado ninguna operación sobre él.
prestar() devuelve True porque pudo realizar la operación. Después, disponible pasa a False.
Como el libro ya está prestado, prestar() devuelve False y conserva el estado actual.
devolver() vuelve a establecer disponible en True. El libro puede prestarse otra vez.
El objeto mantiene su estado
No estamos creando un nuevo libro cada vez que realizamos un préstamo. Trabajamos con la misma instancia, pero sus atributos pueden cambiar.
El atributo disponible describe una situación del objeto. Los métodos prestar() y devolver() permiten modificar esa situación siguiendo reglas definidas en la clase.
En este ejemplo, los métodos permiten expresar con claridad qué significa prestar o devolver un libro. Más adelante veremos que no todas las operaciones necesitan convertirse en métodos.
Comentarios y valoraciones
No hay comentarios aún. ¡Sé el primero en opinar!