Objetivo: presentar el concepto de Objeto o Instancia de una Clase en Python.
En el anterior artículo introdujimos las clases, auténticos pilares de la Programación Orientada a Objetos. Podemos pensar en ellas como diseños de moldes o plantillas para representar en Python (abstraer) una determinada realidad.
Aprendimos, por ejemplo, a modelar un coche:
class Coche:
marca = ''
modelo = ''
color = ''
numero_de_puertas = 0
cuenta_kilometros = 0
velocidad = 0
arrancado = False
def arrancar(self):
if not self.arrancado:
print('Roarrrr')
self.arrancado = True
else:
# Dale al encendido estando el coche arrancado y escucha...
print('Kriiiiiiiiiiicccc')
def parar(self):
self.arrancado = False
def acelerar(self):
if self.arrancado:
self.velocidad = self.velocidad + 1
def frenar(self):
if self.velocidad > 0:
self.velocidad = self.velocidad - 1
def pitar(self):
print('Bip Bip Bip')
def consultar_velocimetro(self):
return self.velocidad
def consultar_cuenta_kilometros(self):
return self.cuenta_kilometros
En la definición de clase nos encontramos sus características o atributos, que toman forma de variables (marca, modelo, color, etc…) y la funcionalidad propia de la clase o métodos, que nos indican qué cosas podemos hacer con ella, representados mediante funciones (arrancar(), acelerar(), frenar(), etc.).
Pero esto no es más que un esquema, un molde. Lo que queremos ahora es fabricar coches concretos, como un Seat León de cinco puertas o un Ford Fiesta de tres.
Al hecho de «fabricar» objetos a partir de un molde de clase se le denomina instanciar, y al producto obtenido se le conoce como instancias o, simplemente, objetos de esa clase.
De modo que vamos a asegurarnos que tenemos los conceptos claros: las clases son los moldes a los que recurrimos después para crear objetos concretos.
Para poner en marcha nuestra fábrica de coches virtual no necesitaremos una gran inversión en infraestructura y personal; nos bastará con ejecutar el intérprete de Python.
Para mayor comodidad, supongamos que hemos preparado ya todo el código con la definición de la clase Coche y lo hemos incluido en un fichero tal como coche.py.
La primera labor consistirá, por lo tanto, en ejecutar dicho código. En mi caso, lo abro en Idle y lo ejecuto con Run, pero tú puedes elegir cualquier otra forma de hacerlo. La cuestión es tener el intéprete interactivo con toda la definición de la clase cargada ya en memoria.
Comprobemos su existencia:
>>> Coche
<class '__main__.Coche'>
Para fabricar coches concretos necesitamos invocar a lo que se conoce como el constructor de clase, que se ocupará de crear el objeto en memoria. El rito para que haga acto de presencia es muy simple: basta con escribir el nombre de la clase seguido de un par de paréntesis.
Construyamos un primer coche:
>>> coche1 = Coche()
Típicamente los paréntesis los utilizaremos para definir valores iniciales del objeto. Más adelante, cuando presentemos el inicializador, aprenderás a usar esa característica.
Parémonos un instante a contemplar nuestra primera fabricación:
>>> coche1
<__main__.Coche object at 0x0234B7D0>
Lo que nos está diciendo el intérprete es que coche1 es un objeto de tipo Coche y está ubicado en la dirección de memoria 0x0234B7D0
.
Cada objeto posee en Python un identicador único que podemos consultar con la función id():
>>> id(coche1)
37009360
En CPython, la implementación más común de Python, este identificador coincide con la dirección de memoria anterior, como podrás comprobar si te tomas la molestia de pasarlo a hexadecimal.
Ya que nos ha ido tan bien, probemos a fabricar un segundo automóvil:
>>> coche2 = Coche()
Aunque de la misma clase, se trata en efecto de un objeto diferente:
>>> id(coche2)
37634800
Para acceder a los atributos y métodos del objeto recurrimos a la notación punto, separando el nombre del objeto del atributo o método mediante un punto.
Establezcamos algunos atributos de coche1:
<code>>>> coche1.marca = "Seat"
>>> coche1.modelo = "León"
>>> coche1.color = "negro"
Y otros tantos de coche2:
coche2.marca = "Ford"
coche2.modelo = "Fiesta"
coche2.numero_de_puertas = 3
Esta forma de proceder, que puede parecer natural, no es la común. Tal como ya hemos apuntado, para dar valores iniciales a los objetos suele emplezarse la figura del inicializador, como veremos más adelante. Lo importante ahora es comprender bien el concepto.
Comprobemos los atributos de coche1:
>>> coche1.marca
'Seat'
>>> coche1.modelo
'León'
>>> coche1.color
'negro'
>>> coche1.numero_de_puertas
0
>>> coche1.cuenta_kilometros
0
>>> coche1.velocidad
0
>>> coche1.arrancado
False
Aprecia como los atributos que no hemos inicializado expresamente toman los valores que tenían en la definición de la clase.
Juguemos ahora un poco con los métodos, funciones que definen lo que podemos hacer con los objetos.
Traigamos a la palestra el primero de ellos. Lo reescribo aquí por comodidad:
def arrancar(self):
if not self.arrancado:
print('Roarrrr')
self.arrancado = True
else:
# Dale al encendido estando el coche arrancado y escucha...
print('Kriiiiiiiiiiicccc')
El parámetro self, tal como explicamos en el artículo anterior, es obligatorio y debe figurar el primero en la declaración del método. Hace referencia al objeto en cuestión sobre el que se aplicará el método.
Para invocar el método, recurrirmos nuevamente a la notación punto, pasando entre paréntesis los argumentos que requiere el método en particular sin contar a self. Como en arrancar sólo existe self, a efectos prácticos es como si no tuviera ninguno:
>>> coche1.arrancar()
Roarrrr
Aunque no esté presente en la invocación, se está pasando implícitamente como self el nombre del método que llama a arrancar(), en este caso, coche1. Observa el código del método. Lo primero que hace es comprobar que el coche no está arrancado, en cuyo caso imprime «Roarrr», cambiando a continuación el atributo arrancado a True. Verifiquemos que en efecto se ha realizado esto último:
>>> coche1.arrancado
True
Naturalmente, coche2 permanece completamente ajeno a estas operaciones, pues arrancar no ha actuado sobre él, sino sobre coche1. Comprobemos que sigue detenido:
>>> coche2.arrancado
False
Estando coche1 arrancado, si intentamos arrancarlo de nuevo obtendremos un chirriante y desagradable ruido propio de forzar el motor de arranque sin necesidad:
>>> coche1.arrancar()
Kriiiiiiiiiiicccc
Revisemos algunos métodos más de la definición de la clase Coche:
def parar(self):
self.arrancado = False
def acelerar(self):
if self.arrancado:
self.velocidad = self.velocidad + 1
def frenar(self):
if self.velocidad > 0:
self.velocidad = self.velocidad - 1
Experimentemos con ellos. Cada vez que aceleramos, la velocidad del vehículo se incrementa en una unidad:
>>> coche1.acelerar()
>>> coche1.velocidad
1
>>> coche1.acelerar()
>>> coche1.velocidad
2
Para detener el vehículo, primero lo frenamos completamente:
>>> coche1.frenar()
>>> coche1.frenar() # Al tener velocidad 2 aplicamos dos veces el método
>>> coche1.velocidad
0
Y, a continuación, sacamos la llave de contacto:
>>> coche1.parar()
>>> coche1.arrancado
False
Es muy importante que estudies con cuidado en estos ejemplos cómo hemos accedido desde los métodos a los atributos del objeto a través de self. En la siguiente lección, cuando presentemos el inicializador, le daremos a esta operatividad un carácter esencial.
Javier Montero Gabarró
El texto de este artículo se encuentra sometido a una licencia Creative Commons del tipo CC-BY-NC-ND (reconocimiento, no comercial, sin obra derivada, 3.0 unported)
El Club del Autodidacta
Consulta el índice completo de artículos relacionados con Python.