Iniciación a la Programación Orientada a Objetos (OOP) con Python – 100 días de Python #11

Iniciación a la Programación Orientada a Objetos (OOP) con Python – 100 días de Python #11

Empezamos fuerte este día número 11 con la llegada de este curso básico de Programación Orientada a Objetos con Python. Un tema fundamental para programar hoy en día.

Este no es más que un solo capítulo de mi curso de 100 días de Python, sin embargo, se puede considerar un mini curso en el que verás los conceptos básicos necesarios para iniciar en la Programación Orientada a Objetos.

¿Qué significa POO?

POO son las siglas en español de Programación Orientada a Objetos. Object Oriented Programming en inglés (OOP).

¿Qué es la programación orientada a objetos?

De la forma más fácil de explicarlo qué se me ocurre, la programación orientada a objetos, es una forma de crear objetos con código para representar cualquier cosa de la vida real e imaginaria.

Ejemplos:

  • Personaje de un videojuego (atributos de vida, fuerza, velocidad, etc.).
  • Usuarios (atributos de nombre, edad, dirección, teléfono, etc.)

Cada objeto que creemos tendrá unas características que lo hagan único. Hagamos una prueba con variables. Mira a tu alrededor. ¿Qué ves?

Yo, por ejemplo, veo una taza. Este es un objeto como otro cualquiera.

¿Qué características tiene este objeto? Voy a escribirlo en variables para describirla mediante código:

color1 = "blanco"
mensaje = None
material = "porcelana"
limpia = True

Vale, ya la he descrito en código. Mensaje lo pongo como None porque es una taza sin ningún mensaje escrito en ella y limpia es para describir el estado de limpieza del objeto. Puesto que está limpia, le he dado un valor de True.

La programación orientada a objetos, funciona de esta forma, solo que no describimos los objetos exactamente de esta forma. ¿Por qué? Por este motivo:

En la mesa tengo otra taza más. Sí, me encanta el café, por eso tantas tazas.

Pues bien, voy a describirla también.

Esta taza posee un color extra, de modo que tiene una propiedad más para ser descrita (color2).

# Objeto taza 1
taza_1_color1 = "blanco"
taza_2_color2 = None
taza_1_mensaje = None
taza_1_material = "porcelana"
taza_1_limpia = True

# Objeto taza 2
taza_2_color1 = "blanco"
taza_2_color2 = "azul"
taza_2_mensaje = "Player 1 - ¿Play again?"
taza_2_material = "porcelana"
taza_2_limpia = False

¿No crees que esto se podría hacer de una forma más eficiente? Estoy repitiendo bastante código y ahora, tengo que nombrar diferente a las variables de cada taza para que no sean reasignados sus valores. ¿Y si tengo un programa que, en lugar de describir dos tazas, describe 100 personajes en un videojuego?

La solución a este problema está en el uso de clases e instanciaciones.

¿Qué son las clases?

Las clases, de forma sencilla, pueden ser descritas como un sistema de generadores de objetos.

En las clases, describimos una forma genérica para crear objetos del mismo tipo a partir de ellas, cada objeto con sus propias características.

Crear una clase en Python

Lo primero antes de crear una clase, es detenerse a pensar ¿Qué características tendrán en común los objetos que cree a partir de esta clase?

En el caso de las tazas, ambas tienen color, ambas pueden llevar mensajes, ambas tienen materiales con los que están fabricadas, ambas pueden estar sucias o limpias.

Todo lo que tienen en común, puede ser aprovechado para escribir código una vez, y no repetirse.

Declarar una clase en Python

Para declarar una clase en Python, tienes que utilizar la palabra reservada "class", darle un nombre y poner los dos puntos de bloque.

class Taza:

El contenido deberá ser indentado como ocurre con los condicionales, funciones o bucles.

Por convención, el nombre de la clase se escribe en PascalCase (primera letra de cada palabra en mayúscula) y no en snake_case como el resto de cosas.

Atributos de clase

Ahora, añadamos las mismas variables que describían al objeto. Tales variables, al estar dentro de una clase, se conocen como atributos de clase.

Estos atributos de clase, sirven para crear un molde de objetos. Va a crear las características que tendrán todos los objetos que se creen a partir de la clase.

Por cierto, la clase se puede especificar con o sin paréntesis (Taza: o Taza():).

class Taza:
    color1 = "blanco"
    color2 = None
    mensaje = None
    material = "porcelana"
    limpia = True

Como puedes ver, tenemos algunas cosas definidas y otras con None (valores vacíos).

¿Qué hacemos con esta clase? Instanciar objetos a partir de ella.

¿Qué es instanciar un objeto?

Llamamos instanciar un objeto, cuando este es generado a partir de una clase. Si yo ahora creo dos objetos de taza, estos serán instancias de la clase taza. Es solo un tecnicismo de la Programación Orientada a Objetos.

¿Cómo se instancia un objeto?

Instanciar un objeto, es muy fácil. Solo tienes que darle un nombre a una variable y asignarle la instanciación, que es simplemente el nombre de la clase con unos paréntesis.

taza_1 = Taza()
taza_2 = Taza()

Perfecto, ya tenemos los dos primeros objetos creados (instanciados) a partir de la clase taza.

¿Dónde se crean los objetos?

Los objetos son creados solo en tiempo de ejecución. Estos se almacenan en la memoria RAM. Una vez finaliza el programa, desaparecen.

Llamemos a los objetos con un print() para ver que sale en la consola o sin él, no es necesario en este caso:

print(taza_1)
print(taza_2)

Resultado en la consola

<main.taza object at 0x000001DD9904B5B0>
<main.taza object at 0x000001DD9904B550>

Lo que nos aparece son las direcciones de memoria donde se ha almacenado el objeto. Ahí pone taza object en la dirección tal. Que podemos traducir como el objeto de clase taza está en la dirección de memoria x.

Esto no nos es útil ahora mismo. Lo que podemos querer ahora, es acceder a los atributos.

¿Cómo se accede a los atributos de un objeto?

Es posible acceder a un atributo del objeto de esta forma:

# Se declara la clase taza
class Taza:
    color1 = "blanco"
    color2 = None
    mensaje = None
    material = "porcelana"
    limpia = True

# Instancia de dos objetos de la clase taza
taza_1 = Taza()
taza_2 = Taza()

# Imprime ciertos atributos
print(taza_1.color1)
print(taza_2.material)

Resultado en la consola

blanco
porcelana

El valor blanco es devuelto por el atributo "color1" que tiene el objeto "taza_1" y porcelana pertenece al atributo "material" del objeto "taza_2".

Así de fácil accedemos a los atributos de los objetos.

Valores por defecto en clases

Si te has fijado, ambos objetos tienen los mismos atributos y valores. Entonces, ¿para qué queremos estos objetos repetidos?

Estos valores y atributos, son los que tienen todos los objetos de tipo taza por defecto en mi programa.

Reasignar valores a atributos de objetos

A partir de la instanciación, podemos variar los valores de los atributos, recuerda que en realidad son variables.

La taza dos, es de color blanco, pero tiene un segundo color. Entonces, le voy a cambiar el valor por defecto que lleva de None por "azul".

¿Ves aquí? En la línea 13, he accedido al atributo que quería del objeto en concreto que quería y le he cambiado el valor. Esta acción, como verás en los print(), solo afecta al atributo del propio objeto, no cambia nada en la clase o en otros objetos instanciados de ella.

# Se declara la clase taza
class Taza:
    color1 = "blanco"
    color2 = None
    mensaje = None
    material = "porcelana"
    limpia = True

# Instancia de dos objetos de la clase taza
taza_1 = Taza()
taza_2 = Taza()

taza_2.color2 = "azul"

# Imprime ciertos atributos
print(taza_1.color2)
print(taza_2.color2)

Resultado en la consola

None
azul

Ahora que sabes esto, puedes imaginarte, crear una clase en un videojuego de mundo abierto, donde hay ciudadanos. Creas una clase llamada ciudadano y le das unos atributos que sean comunes para todos.

A partir de esta base, vas instanciando ciudadanos y les vas haciendo variaciones. Color de pelo, de ojos, altura, vestimenta, etc.

Así, escribes el código general para cada ciudadano y luego solo haces pequeños cambios en cada uno para diferenciarlos.

Los métodos de las clases de Python

Las clases están formadas en realidad por métodos y atributos. Hasta ahora, solo hemos usado en la clase los atributos.

¿Qué son los métodos?

A lo largo de este curso, me has oído (leído si no ves mis vídeos) decir muchas veces la palabra método. El método index(), el método pop(), etc.

Sencillamente, los métodos son funciones que al estar dentro de una clase, se llaman métodos.

Entonces, los métodos mencionados, son métodos pertenecientes a la clase list de Python.

El núcleo de Python

Escribe lo siguiente (estoy en VSCode) en un archivo .py:

list

A continuación, haz click derecho sobre la palabra y selecciona la opción "Mostrar jerarquía de llamadas". Te aparecerá esto en el menú de referencias. Haz doble click sobre el elemento marcado.

clase list python

Podrás ver que las listas que creamos, son objetos instanciados de la clase "list" de Python, la cual, tiene, como puedes ver en la imagen, una serie de funciones con "def" que se llaman métodos al estar creados dentro de una clase.

Programación orientada objetos python

Si utilizamos type() para ver el tipo de variable, veremos que nos sale de una lista, clase list:

numeros = [1,3,5,7]

print(type(numeros))

Resultado en la consola

<class 'list'>

Otro ejemplo, el método (en realidad clase) range(), ¿Recuerdas que podía tener 3 argumentos? Uno para el inicio del rango (start), otro para el final (stop) y otro para los saltos o pasos (step), aquí están:

clase range de Python

Lo mismo con esto, si miramos el tipo, nos devuelve la clase:

rango = range(10)

print(type(rango))

Resultado en la consola

<class 'range'>

Ves haciendo la prueba con otros objetos como un string, un integer, un booleano...

Por lo que ahora te puedo decir, que todo lo que construyes es Python, son objetos.

Bueno, volvemos al tema inicial. Vamos a ver como añadir métodos a las clases.

Declarar un método en una clase de Python

Para declarar un método en una clase de Python, lo haremos igual que con las funciones.

¿Qué método se le puede añadir a una taza?

Los métodos están pensados para que los objetos realicen acciones, por lo tanto, quizás una taza sea un mal ejemplo para esto.

Vamos a representar algo clásico en la enseñanza de la programación orientada a objetos, un vehículo.

Como puedes ver, he creado dos métodos que realizan dos acciones. Ahora, todos los objetos de tipo "vehiculo" que cree, van a tener esos métodos sin tener que repetir código.

Estas acciones están representadas con un print(), pero si fuese un videojuego, pondríamos en el método, el código necesario para que el vehículo arrancase, hiciese el sonido y quizás alguna animación de vibración.

# Se declara la clase vehiculo
class Vehiculo():
    # Atributos
    color = None
    longitud_metros = None
    ruedas = 4

    # Métodos
    def arrancar(self):
    	print("El vehículo ha arrancado.")

    def detener(self)
        print("El vehículo está detenido.")
    
# Instancia de dos objetos de la clase vehiculo
vehiculo_1 = Vehiculo()

# llamadas a métodos
vehiculo.arrancar()
vehiculo.detener()

El objeto puede cambiar cuando quiera de esta, se puede arrancar y detener cuando queramos llamando a los métodos como ves en las líneas 19 y 20.

Resultado en la consola

El vehículo ha arrancado.
El vehículo está detenido.

Creación de atributos desde fuera de una clase en Python

Python es capaz de crear atributos que no existen en una clase, para un objeto en concreto.

Creemos otro vehículo más:

# Se declara la clase vehiculo
class Vehiculo():
    # Atributos
    color = None
    longitud_metros = None
    ruedas = 4

    # Métodos
    def arrancar(self):
        print("El vehículo ha arrancado.")

    def detener(self):
        print("El vehículo está detenido.")

# Instancia de dos objetos de la clase taza
vehiculo_1 = Vehiculo()
vehiculo_2 = Vehiculo()

El "vehiculo_2" cuenta con alerón, así que necesita un atributo extra, en cambio, no quiero que lo tenga el "vehiculo_1", por lo tanto, hago esto:

vehiculo_2.material_aleron = "Fibra de carbono"

Probemos de acceder a este nuevo atributo que no existe en la clase.

print(vehiculo_2.material_aleron)

Resultado en la consola

Fibra de carbono

Excelente. Se ha creado correctamente.

Ahora, intenta acceder a este atributo con "vehiculo_1".

print(vehiculo_1.material_aleron)

Error en la consola

AttributeError: 'vehiculo' object has no attribute 'material_aleron'

Nos da un error de atributo. Dice, el objeto de la clase "vehiculo" no tiene el atributo 'material_aleron'.

Cuando intentes acceder a algo de una clase que no tiene un atributo en concreto, te va a dar este error.

Por ejemplo, la clase tuple (tupla), no cuenta con métodos de lista como pop(). Si pruebas de utilizarlo, te dará el mismo error:

numeros = (10,95,37,34)

numeros.pop()

Error en la consola

AttributeError: 'tuple' object has no attribute 'pop'

Con todo lo que te estoy mostrando en este décimo primer día, estás ya consolidando una base muy sólida para continuar con Python. Después de la programación orientada a objetos, todos los temas que vayamos tratando, serán un poco más fáciles de entender.

El método __init__ de Python

Llegamos a un tema que deja muchos quebraderos de cabeza en Python, y no debería ser así, ya que es bastante más sencillo de entender de lo que parece.

¿Qué es el método __init__?

El método __init__ es un método constructor (construye algo) especial de Python, para construir unos valores iniciales en los objetos instanciados. Lo que se llama como inicializar los objetos.

Fíjate en los atributos de la clase "vehiculo":

color = None
longitud_metros = None
ruedas = 4

Estos atributos funcionan, pero de la forma en que los he puesto, sin utilizar el método __init__, son valores estáticos que solo pueden ser variados con asignación posterior a la instanciación de un objeto. Ejemplo:

vehiculo_1.longitud_metros = 4

Sin embargo, gracias al método __init__, puedo establecer ciertos atributos que serán especificados en la instanciación como argumentos. Veamos el código y te explico.

class Vehiculo():

    def __init__(self, color, longitud_metros, ruedas):
        self.color = color
        self.longitud = longitud_metros
        self.ruedas = ruedas

    def arrancar():
        print("El vehículo ha arrancado.")

    def detener():
        print("El vehículo está detenido.")
    

# Instancia de dos objetos de la clase vehiculo
vehiculo_1 = Vehiculo("rojo", 4, 4)
vehiculo_2 = Vehiculo("negro", 4.10, 8)

print(vehiculo_1.color)
print(vehiculo_2.color)

¿Qué es self?

Fíjate en el primer parámetro (self) del método __init__. Este self se refiere al objeto que será instanciado de esa clase. Es como un comodín que toma como valor la propia referencia en memoria de cada objeto.

Esto no es muy ilustrativo si no has tocado nunca la POO. Así que, voy a tratar de explicarlo de manera más sencilla.

self es un parámetro que tiene que estar siempre en el método __init__ y otros métodos de clase. Lo pondremos al principio. Este se llama self por convención, porque aunque cambiemos self por cualquier otra cosa, toma el primer argumento como si fuera self.

Por supuesto, si cambia en la declaración de parámetros, el resto de self de los atributos, deben ser nombrados igual. Ejemplo:

def __init__(objeto, color, longitud_metros, ruedas):
	objeto.color = color
    objeto.longitud_metros = longitud_metros
    objeto.ruedas = ruedas

Personalmente, no cambiaría el self, ya que si alguien tiene que editar tu código, le vas a complicar más la tarea de leerlo y entenderlo.

El parámetro self apunta al objeto concreto en la que se llama al método. Se utiliza dentro del método para acceder a atributos y otros métodos de la propia instancia.

De forma más simple todavía. En cada objeto se sustituye internamente el valor self por el nombre del objeto. De manera que no tengamos que crear atributos específicos para cada objeto.

Recuerda el ejemplo de las variables con los objetos de taza. Mediante este sistema, (no son objetos de verdad, solo era una representación) tengo que poner a cada objeto, el nombre del objeto (taza_1 o taza_2) y el atributo. Con self, el mismo atributo me sirve para tantos objetos como cree con el mismo atributo.

# Objeto taza 1
taza_1_color1 = "blanco"
taza_2_color2 = None
taza_1_mensaje = None
taza_1_material = "porcelana"
taza_1_limpia = True

# Objeto taza 2
taza_2_color1 = "blanco"
taza_2_color2 = "azul"
taza_2_mensaje = "Player 1 - ¿Play again?"
taza_2_material = "porcelana"
taza_2_limpia = False

Usando el ejemplo de los vehículos, sería algo como vehiculo_1.color, vehiculo_2.color. En cambio, si utiliza self.color, ese self se reemplaza solo en cada objeto por vehiculo_1 o vehiculo_2 o cualquier otro nombre que tengan los nuevos objetos.

Argumentos justos para parámetros en instanciaciones con __init__

Ahora que tenemos el método __init__, cuando realices una instanciación, deberás hacerlo con el número exacto de argumentos, al igual que ocurre con las funciones. Si tenemos 3, hay que pasarle 3 exactamente. Si no lo hacemos, recibiremos el mostrado anteriormente, AttributeError.

¿Por qué se repiten los nombres de atributo en el __init__?

Veamos el motivo por el cual se repiten los nombres de atributo en el __init__ (ejemplo: color = color).

 def __init__(self, color, longitud_metros, ruedas):
        self.color = color
        self.longitud = longitud_metros
        self.ruedas = ruedas

Por un lado, tenemos la declaración de parámetros, color, longitud_metros y ruedas en los paréntesis. El self, ignóralo, ese argumento lo utiliza el intérprete de Python, no tenemos que asignarle ningún valor en la instanciación de los objetos.

Por un lado, tenemos el nombre del atributo, que es el de la izquierda, el que va con el 'self.' y el otro, es el parámetro asociado al atributo, el cual, recibirá los valores pasados como argumentos a los parámetros en la instanciación de cada objeto.

Ya sé que es algo complicado de entender, pero es necesario.

Básicamente, el nombre de atributo, no tiene por qué ser igual al del parámetro.

Mira este ejemplo, he cambiado el valor de nombre de atributo y ahora, para llamar al color, tengo que llamar al atributo a.

class Vehiculo():

    def __init__(self, color, longitud_metros, ruedas):
        self.a = color
        self.b = longitud_metros
        self.c = ruedas

    def arrancar(self):
        print("El vehículo ha arrancado.")

    def detener(self):
        print("El vehículo está detenido.")
    
# Instancia de dos objetos de la clase vehiculo
vehiculo_1 = Vehiculo("rojo", 4, 4)
vehiculo_2 = Vehiculo("negro", 4.10, 8)

print(vehiculo_1.a)

Resultado en la consola

rojo

Es más frecuente poner el nombre de atributo igual al del valor para que sea más fácil de identificar cada valor con el respectivo atributo.

def __init__(self, parámetro1, parámetro2, parámetro3):
	self.atributo1 = parámetro1
    self.atributo2 = parámetro2
    self.atributo3 = parámetro3

Volvamos al código anterior:

class Vehiculo():

    def __init__(self, color, longitud_metros, ruedas):
        self.color = color
        self.longitud = longitud_metros
        self.ruedas = ruedas

    def arrancar(self):
        print("El vehículo ha arrancado.")

    def detener(self):
        print("El vehículo está detenido.")
    
# Instancia de dos objetos de la clase vehiculo
vehiculo_1 = Vehiculo("rojo", 4, 4)
vehiculo_2 = Vehiculo("negro", 4.10, 8)

print(vehiculo_1.color)
print(vehiculo_2.color)

Entonces, lo que pasamos en la instanciación (líneas 15 y 16), son 3 argumentos para los 3 parámetros del método inicializador (__init__). Estos valores son asignados a los atributos correspondientes asociados con self.atributo.

Estas instanciaciones, equivaldrán a esto:

# Instanciación objeto vehiculo_1
self.color = "rojo"
self.longitud_metros = 4
self.ruedas = 4

# Instanciación objeto vehiculo_2
self.color = "negro"
self.longitud_metros = 4.10
self.ruedas = 8

Y lo que Python le pasará al self sería esto:

# Instanciación objeto vehiculo_1
vehiculo_1.color = "rojo"
vehiculo_1.longitud_metros = 4
vehiculo_1.ruedas = 4

# Instanciación objeto vehiculo_2
vehiculo_2.color = "negro"
vehiculo_2.longitud_metros = 4.10
vehiculo_2.ruedas = 8

Así, después podemos llamar a los atributos como hemos hecho desde el principio. Ejemplo:

print(vehiculo_1.color)

Ya sé que esto es muy complicado si no has hecho nunca programación orientada a objetos en otros lenguajes de programación. Lo que quiero es que todo el mundo que se esté iniciando, sepa por el momento, crear una clase, instanciar objetos de ella, acceder a sus atributos y modificarlos.

Valores fijos en el inicializador

En el método __init__, no es obligatorio declarar todos los parámetros para todos los atributos. Por ejemplo, se puede poner un valor fijo si queremos que todos los objetos tengan un valor por defecto.

Supongamos que una empresa importa solo coches fabricados en Alemania, el país de origen siempre será ese de todos los vehículos, excepto algunas ocasiones. Entonces, en un supuesto programa, no habría que introducir el país de origen, ya que casi siempre sería el mismo.

Para el ejemplo, fíjate en la línea 7, la cual, tiene un valor que se va a asignar automáticamente a cada objeto instanciado. Nosotros solo podremos especificar en la instanciación, el color, la longitud y el número de ruedas.

class Vehiculo():

    def __init__(self, color, longitud_metros, ruedas):
        self.color = color
        self.longitud = longitud_metros
        self.ruedas = ruedas
        self.pais_origen = "Alemania"

    def arrancar(self):
        print("El vehículo ha arrancado.")

    def detener(self):
        print("El vehículo está detenido.")
    

# Instancia de dos objetos de la clase vehiculo
vehiculo_1 = Vehiculo("rojo", 4, 4)
vehiculo_2 = Vehiculo("negro", 4.10, 8)

print(vehiculo_1.pais_origen)
print(vehiculo_2.pais_origen)

Resultado en la consola

Alemania
Alemania

Si resulta que "vehiculo_2" no es de Alemania, no hay problema, se le asigna otro valor a su atributo:

vehiculo_2.pais_origen = "USA"
print(vehiculo_2.pais_origen)

Resultado en la consola

USA

Atributos de instancia y de clase

Hay una pequeña diferenciación entre el atributo de la línea 3 y los del método __init__.

Los atributos que están dentro de la clase, se llaman atributos de clase (pais_origen). Los atributos de instancia son los que se crean para el __init__.

Por este motivo, yo los colocaría así, dado que "pais_origen" no es un atributo que necesitemos inicializar:

class Vehiculo():
  
  	pais_origen = "Alemania"

    def __init__(self, color, longitud_metros, ruedas):
        self.color = color
        self.longitud = longitud_metros
        self.ruedas = ruedas
        

Uso de self en los métodos normales

En los métodos normales, también hay que hacer uso de self. Hasta ahora, arrancar() y detener(), no tienen más que un print() y no utilizan atributos. Sin embargo, es necesario. Probemos sin el self. (líneas 10 y 13).

class Vehiculo():
	# Atributos de clase
    pais_origen = "Alemania"
    
	# Métodos
    def __init__(self, color, longitud_metros, ruedas):
        self.color = color
        self.longitud_metros = longitud_metros
        self.ruedas = ruedas

    def arrancar():
        print("El vehículo ha arrancado.")

    def detener():
        print("El vehículo está detenido.")   

# Instancia de dos objetos de la clase vehiculo
vehiculo_1 = Vehiculo("rojo", 4, 4)
vehiculo_2 = Vehiculo("negro", 4.10, 8)

vehiculo_1.arrancar()

Error en la consola

TypeError: Vehiculo.arrancar() takes 0 positional arguments but 1 was given

Este TypeError, indica que tiene 0 valores como argumentos posicionales y se le ha pasado uno.

Esto es lo que hace Python con el self. El self lo pasa siempre, nosotros no le tenemos que indicar ningún valor. Sin embargo, en este caso, si no ponemos self en la declaración del método, nos produce este error al pasarlo como argumento. Así que ten en cuenta que lo tienes que tener.

Además, si ahora creamos un método que utilice parámetros de clase o de instancia, no podríamos sin self.

Añado el siguiente método para mostrar información sobre los vehículos. Le voy a poner el parámetro self para evitar el error de antes.

Voy a intentar llamar a los atributos sin utilizar self. Así:

def mostrar_info(self):
	print(f"Vehículo de color {color}. Con una longitud de {longitud_metros}. Tiene {ruedas} ruedas.")

El programa ejecuta y toda la clase funciona, el problema estará cuando intente llamar al método "mostrar_info()".

Error en la consola

NameError: name 'color' is not defined

Me da un error por nombre no definido. En este caso con el primero que encuentra, pero con el resto pasará lo mismo.

Eso es porque hay que utilizar self como en el __init__.

class Vehiculo():
    # Atributos de clase
    pais_origen = "Alemania"

    # Métodos
    def __init__(self, color, longitud_metros, ruedas):
        self.color = color
        self.longitud_metros = longitud_metros
        self.ruedas = ruedas

    def arrancar(self):
        print("El vehículo ha arrancado.")

    def detener(self):
        print("El vehículo está detenido.")
    
    def mostrar_info(self):
	    print(f"Vehículo de color {self.color}. Con una longitud de {self.longitud_metros}. Tiene {self.ruedas} ruedas.") 

# Instancia de dos objetos de la clase vehiculo
vehiculo_1 = Vehiculo("rojo", 4, 4)
vehiculo_2 = Vehiculo("negro", 4.10, 8)

vehiculo_1.mostrar_info()
vehiculo_2.mostrar_info()

Ahora sí, puedo llamar al método y acceder a los atributos que necesite, tanto si son de clase o de instancia.

Dejar una clase vacía

Cuando estamos en fase de creación de un programa, ya tendremos descritas las clases que lo van a formar (todas o casi todas). El problema viene cuando quieres dejar las clases definidas, pero ir añadiendo código más adelante. Aquí tienes dos opciones, las comentas y todos los objetos instanciados también (si los hay) o utilizas la palabra reservada "pass".

Si dejas una clase vacía, ocurre el siguiente error:

class Vehiculo():
    

Error en la consola

IndentationError: expected an indented block after class definition on line 1

Error de indentación, se esperaba un bloque indentado después de la definición de clase de la línea 1.

Utiliza "pass" así y problema resuelto. Cuando quieras escribir algo para la clase, lo borras y pones el código.

class pass Python

class Vehiculo():
    pass

Ya que hemos llegado al tema del "pass", debo indicar, que las otras estructuras como bucles, condicionales y funciones, también lo pueden usar.

If pass Python

El True no es necesario, solo lo he puesto para no ponerle una expresión de comparación al if. Se puede dejar con la expresión que quieras.

if True:
    pass

for pass Python

for i in range(0):
    pass

while pass Python

while True:
    pass

def pass Python

def funcion():
    pass

Con las funciones lambda no se puede utilizar:

lambda : pass

Sin embargo, con que le pongamos cualquier expresión como True, no nos va a dar error.

lambda : True

Espero que con todo esto, consigas una buena base para empezar a aprender a fondo la programación orienta a objetos con el maravilloso lenguaje que es Python.

Seguiremos viendo nuevos temas en el curso más avanzados que esto.

Soy consciente que la programación orientada a objetos es un tema complicado cuando empiezas con él la primera vez. Si es tu caso, te recomiendo encarecidamente que veas el vídeo para aprenderlo más fácil de manera guiada (si no lo has hecho ya claro). Y por supuesto, si te quedan dudas relacionadas con este mini curso, déjame un comentario.

Pasemos ya a los ejercicios con la Programación Orientada a Objetos de Python.

Un comentario en «Iniciación a la Programación Orientada a Objetos (OOP) con Python – 100 días de Python #11»

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

curso de Python Entrada anterior Soluciones de ejercicios – 100 días de Python #10
curso de Python Entrada siguiente Ejercicios de Programación Orientada a Objetos en Python – 100 días de Python #11