Variables de Clase y Variables de Instancia en Python

Variables de clase y de instancia en Python

Esta es una duda común y general cuando nos adentramos en la programación orientada a objetos. La he tenido yo, la estarás teniendo tú y le pasará a mucha más gente. En este artículo te explico las diferencias entre las variables de clase y las variables de instancia y cómo se trabaja con ellas en Python.

Una variable de clase es única y compartida por todas sus instancias. Una variable de instancia es exclusiva y particular de cada instancia. En Python, las variables de clase se definen fuera de los métodos y las de instancia dentro de ellos.

A continuación te explico en detalle las diferencias entre estos dos tipos de variables y te planteo diversos ejemplos para que no te quede ninguna duda.

Principales diferencias

Lo primero que tienes que tener claro es la diferencia entre estos dos tipos de variables. Te lo voy a explicar con un símil: los seres humanos. El concepto de ser humano todos lo tenemos claro. Es un concepto bien definido y es único, es decir, no hay dos conceptos diferentes para «ser humano».

En cambio, individuos de la especie humana, es decir, seres humanos, hay muchos, infinidad de ellos, y cada uno diferente.

Pues bien, siguiendo este símil, cualquier propiedad o información atribuible al concepto de ser humano se recoge en variables de clase y son comunes a todos los individuos.

Por otro lado, cualquier propiedad o información atribuible a cada uno de los individuos y particular a cada uno de ellos se recoge en variables de instancia.

Por ejemplo, una característica común a todos los seres humanos y que, por tanto, pertenece al concepto de ser humano, puede ser el número de brazos, que siempre es dos (salvo accidente y en condiciones de normalidad). Así, el concepto de ser humano tendría una variable de clase con valor 2 que refleja el número de brazos.

Una característica de cada uno de los individuos de la especie humana es el nombre. Todos tienen y además es particular de cada uno, pues en cada uno es diferente (aunque pueda haber coincidencias). Así, tendríamos una variable de instancia con el nombre en cada ser humano.

En resumidas cuentas, el concepto de ser humano es la clase y cada uno de los seres humanos son las instancias.

Variables de instancia

También llamadas atributos o variables de objeto, estas variables representan la información particular de cada una de las instancias de una clase, como por ejemplo, el nombre de cada ser humano, el color de una silla o el importe total de una factura.

Cuando creamos una clase en Python, lo más común es inicializar los atributos o variables de instancia en el método de inicialización __init__, aunque también podrían crearse variables de instancia en otro métodos de instancia.

Vamos a verlo con un ejemplo. Crearemos una clase para representar un semáforo de tráfico. La información que nos interesa almacenar es su estado actual, que es su color, es decir, si su luz está en rojo, en amarillo o en verde. Con esto creamos la clase Semaforo inicializando su variable luz a rojo (por defecto cualquier semáforo recién creado tendrá su luz en rojo). Proporcionaremos además un método cambiar para poder cambiar su luz cuando lo necesitemos:

class Semaforo:

	def __init__(self):
		self.luz = 'Rojo' #creamos e inicializamos la variable de instancia

	def cambiar(self, nuevo_estado):
		self.luz = nuevo_estado #cambiamos el valor de la variable de instancia

Desde el momento en que la variable está creada e inicializada podemos acceder a ella a través del nombre del objeto o de la instancia. Por supuesto, previamente hay que crear dicha instancia. Además, podremos invocar a la función de cambio de estado para alterar el valor de dicha variable. A continuación creamos dos instancias diferentes, cambiamos la luz a una de ellas y mostramos por pantalla el color de la luz de cada instancia:

#creamos dos instancias
un_semaforo = Semaforo() 
otro_semaforo = Semaforo()

#cambiamos el valor de la variable de instancia luz de una de las instancias
un_semaforo.cambiar('Verde') 

#mostramos por pantalla el valor de las variables de cada instancia
print(un_semaforo.luz)
print(otro_semaforo.luz)

El código anterior generará el siguiente resultado por pantalla:

Verde
Rojo

Es importante notar que, en el ejemplo anterior, existen dos instancias diferentes de la misma clase, es decir, dos semáforos diferentes, donde cada uno mantiene su luz de manera individual. Modificar una variable de instancia no implica en ningún caso un cambio en las variables de otras instancias.

Como puedes ver, para acceder a una variable de instancia desde dentro de la propia clase se utiliza la palabra reservada self que hace referencia a la instancia particular que está ejecutando el código definido en la clase.

Variables de clase

También se llaman atributos de clase o, a veces, variables estáticas (que no tiene nada que ver con constantes, pues su valor se puede modificar). Las variables de clase representan información que es común para todas las instancias de una clase.

De esta manera podemos tener diversas instancias de una clase y todas ellas compartirán los valores de las variables de clase. Si una instancia modifica el valor de una variable de clase, dicho valor queda modificado para todas las instancias.

Así, cuando necesitamos compartir un valor común para todas las instancias de una clase definiremos una variable de clase.

La manera más sencilla de definir una variable de clase es hacerlo dentro de la clase pero fuera de las funciones. Para ver esto vamos a definir una clase para representar perros. Cada perro tendrá su nombre, que como ya sabes, podrá ser representado por una variable de instancia, pero además queremos guardar el tipo de animal que es, en este caso, mamífero. Como todos los perros son mamíferos indiscutiblemente, esta valor lo guardaremos en una variable de clase:

class Perro:

	tipo_de_animal = 'Mamífero' #se define la variable de clase

	def __init__(self, nombre):
		self.nombre = nombre #se define la variable de instancia

Se puede acceder al valor de una variable de clase partiendo de la propia clase o de una instancia de dicha clase, como veremos a continuación:

#creamos dos instancias de Perro
luna = Perro('Luna')
golfo = Perro('Golfo')

#mostramos la información de las variables de clase y de instancia
print(f'{luna.nombre} es un {luna.tipo_de_animal}') #acceso a través de la instancia
print(f'{golfo.nombre} es un {golfo.tipo_de_animal}')

print(f'Todos los perros son de tipo {Perro.tipo_de_animal}') #acceso a través de la clase

La ejecución del código anterior mostrará el siguiente resultado por pantalla:

Luna es un Mamífero
Golfo es un Mamífero
Todos los perros son de tipo Mamífero

Como ves, se puede acceder de manera muy cómoda e indistintamente al valor de una variable de clase o de instancia a partir del objeto. Pero esto plantea el siguiente problema.

¡Cuidado! Si intentas modificar el valor de una variable de clase a través de una instancia o del objeto self desde dentro de la propia clase tendrás un resultado inesperado. La variable de clase no se modificará y se creará una variable de instancia con el mismo nombre que la variable de clase y con el valor asignado.

Esto, además, provoca un problema de shadowing donde si intentamos acceder al valor de la variable de clase obtendremos el valor de la variable de instancia creada. Para evitar esto es aconsejable acceder a las variables de clase a través de la clase.

Ejemplo de uso simultáneo de variables de clase y de instancia

Te voy a plantear un ejemplo en el que se usen simultáneamente ambos tipos de variables y donde el valor de ambos puede verse modificado.

Supongamos que necesitamos un sistema para registrar la temperatura ambiental media de un conjunto de viviendas. Cada vivienda tiene una variable de instancia con su temperatura media en cada momento, por lo que puede ir cambiando su valor al cambiar la temperatura. Además necesitamos saber y almacenar cuál es el máximo de las temperaturas para el conjunto de todas las viviendas. Para esto utilizaremos una variable de clase.

Es importante notar que si cambia la temperatura de una vivienda es posible que esta supere el máximo actual, con lo que habría que modificar la variable de clase para registrar este cambio.

Veamos entonces una posible solución a este problema:

class Vivienda:

	temperatura_maxima = -100 #inicializamos a un valor ficticio muy pequeño

	def __init__(self, temperatura):
		self.temperatura_actual = temperatura
		self.registrar_temperatura(temperatura)

	def registrar_temperatura(self, nueva_temperatura):
		self.temperatura_actual = nueva_temperatura
		# si alguna vivienda supera la temperatura máxima se actualiza la variable de clase
		if self.temperatura_actual > self.temperatura_maxima:
			self.__class__.temperatura_maxima = self.temperatura_actual

Fíjate no solo en la variable de clase temperatura_maxima, sino que hazlo también en la variable de instancia temperatura_actual. Dicha variable no está definida en la función de inicialización directamente, se inicializa en la función registrar_temperatura la primera vez que es invocada. Esta primera invocación sí que tiene lugar en la función de inicialización.

La variable de clase se inicializa con un valor ficticio muy bajo, -100 en este caso. ¿Por qué hago esto? Muy fácil, a la hora de calcular un máximo es importante partir de un número muy bajo para poder considerar en este cálculo cualquier número real bajo. En este caso sabemos que ninguna temperatura en una vivienda puede ser tan baja como -100, así, la primera vez que se registre una temperatura, por baja que sea (por ejemplo, -5 grados centígrados), siempre será mayor que nuestra temperatura inicial de -100. De esta forma, ya quedará guardada como máxima la primera temperatura que se registre en una vivienda.

Cada vez que se registre una nueva temperatura se comprobará si dicha temperatura sobrepasa el máximo registrado en la variable de clase y se modificará en ese caso.

Para terminar de clarificar el ejemplo, vamos a crear algunas viviendas, a registrar distintas temperaturas y, finalmente, a mostrar la temperatura de cada vivienda y el máximo de todas ellas:

#creamos tres viviendas y registramos su temperatura inicial
v1 = Vivienda(20)
v2 = Vivienda(23)
v3 = Vivienda(18)

#mostramos la temperatura máxima
print(f'La temperatura máxima es {Vivienda.temperatura_maxima}')

#registramos algunas temperaturas nuevas
v1.registrar_temperatura(25)
v2.registrar_temperatura(22)
v3.registrar_temperatura(21)

#mostramos las temperaturas de las viviendas y la temperatura máxima
print(f'Temperatura vivienda 1: {v1.temperatura_actual}')
print(f'Temperatura vivienda 2: {v2.temperatura_actual}')
print(f'Temperatura vivienda 3: {v3.temperatura_actual}')
print(f'La temperatura máxima es {Vivienda.temperatura_maxima}')

Al ejecutar el código anterior obtenemos el siguiente resultado por pantalla:

La temperatura máxima es 23
Temperatura vivienda 1: 25
Temperatura vivienda 2: 22
Temperatura vivienda 3: 21
La temperatura máxima es 25

Espero que este artículo te haya clarificado algunos conceptos y ya no tengas ninguna duda sobre los principios básicos de las variables de instancia y las variables de clases en Python. Ahora… ¡a practicar! ¡No te quedes ahí inmóvil!

La Hoja de Referencia de Python – ¡Gratis!

La Hoja de Referencia de Python - Código Pitón
Consigue trucos, consejos y actualizaciones y, por supuesto, la Hoja de Referencia de Python gratis.



Antes de suscribirte consulta aquí la Información Básica sobre Protección de Datos. Responsable de los datos: Laura Otero Moreira. Finalidad de la recogida y tratamiento de los datos personales: enviarte boletín informativo de Python y comunicaciones comerciales. Legitimación: tu consentimiento. Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (MailRelay). Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control. Más información en nuestra política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal, incluida información sobre acceso, conservación, rectificación, eliminación, seguridad y otros temas.