Ir al contenido
wannacode

Comprobaciones de tipo

En resumen: Una comprobación de tipo con isinstance pregunta si un objeto es de una clase dada o de una de sus clases hijas, y type(x) is pregunta por la clase exacta.

Problema

Solo algunos se pueden cargar

Una empresa tiene una flota de coches normales y eléctricos. Cada noche hay que cargar los eléctricos. ElectricCar hereda de Car y añade charge(), así que todos están en una sola lista:

fleet = [
Car("Skoda"),
ElectricCar("Tesla", 35),
Car("Dacia"),
ElectricCar("Nissan", 60),
]
for c in fleet:
c.charge()

El programa arranca y se detiene en el primer coche, «Skoda»: AttributeError: 'Car' object has no attribute 'charge'. Nunca llega a los eléctricos.

Un coche normal de verdad no se puede cargar. Tienes que preguntar a cada coche: «¿eres eléctrico?»

La idea

Pregunta al objeto qué es

for c in fleet:
if isinstance(c, ElectricCar):
c.charge()
print(f"{c.brand}: cargado al {c.battery}%")
Salida
Tesla: cargado al 100%
Nissan: cargado al 100%

isinstance(c, ElectricCar) pregunta al objeto al que apunta c: «¿eres un ElectricCar?» y da True o False. Esto se llama comprobación de tipo.

No necesitas una variable nueva. En Python las variables no tienen tipo, así que no hay nada que convertir: en cuanto la comprobación es verdadera, c apunta a un coche eléctrico y c.charge() funciona. Los coches siguen siendo los mismos.

for c in fleet:
if isinstance(c, ElectricCar):
c.charge()
print(f"{c.brand}: cargado al {c.battery}%")
Paso a paso, en palabras
  1. fleet es una lista: 2 coches normales y 2 eléctricos.
  2. c apunta al Skoda: isinstance da «no». El cuerpo del if se salta.
  3. c apunta al Tesla: isinstance da «sí». No hace falta una variable nueva: c.charge() carga el mismo coche.
  4. c apunta al Dacia: isinstance da «no». El cuerpo del if se salta.
  5. c apunta al Nissan: isinstance da «sí». No hace falta una variable nueva: c.charge() carga el mismo coche.
  6. Los coches no cambian. La comprobación solo pregunta al objeto qué es.

Pruébalo

¿Es un coche?

¿Qué imprimirá el programa?

c = ElectricCar("Tesla", 35)
d = Car("Skoda")
print(isinstance(c, Car))
print(isinstance(c, ElectricCar))
print(isinstance(d, ElectricCar))
Respuesta

La idea

Otras formas con las que te encontrarás

Además de isinstance te encontrarás con dos maneras más de preguntar a un objeto:

c = fleet[1]
print(type(c) is ElectricCar)
print(type(c) is Car)
print(isinstance(c, Car))
print(hasattr(c, "charge"), hasattr(fleet[0], "charge"))
Salida
True
False
True
True False
  • type(c) is ElectricCar pregunta «¿es exactamente un ElectricCar?». Para el coche eléctrico type(c) is Car es False, mientras que isinstance(c, Car) es True. Casi siempre quieres isinstance: también acepta las clases hijas.
  • hasattr(c, "charge") no pregunta por la clase, solo si el objeto tiene un método así. Es una comprobación contra un contrato, como con las interfaces: pasa cualquier clase con charge().
Más precisamente, para curiosos
  • La comprobación siempre es falsa para None, e isinstance también acepta varias clases a la vez, entre paréntesis: es verdadera si el objeto pertenece a cualquiera de ellas.
print(isinstance(None, Car))
print(isinstance(fleet[1], (Car, ElectricCar)))
Salida
False
True
  • La comprobación también funciona con contratos: isinstance(a, Swimmable) pregunta si la clase del objeto ha aceptado la clase abstracta Swimmable.

Pruébalo

Encuentra el error

El programa cuenta los coches normales y los eléctricos. La flota tiene dos de cada uno, pero imprime:

Salida
Normales: 4, eléctricos: 0

¿En qué línea está el error?

Elige la línea con el error.

La idea

¿Una comprobación o un método?

Quieres imprimir con qué funciona cada coche. Una comprobación de tipo puede hacerlo:

for c in fleet:
if isinstance(c, ElectricCar):
print(f"{c.brand}: electricidad")
else:
print(f"{c.brand}: gasolina")

Pero esto es el salario con un if por cada rol de la lección de polimorfismo. Todo coche tiene una fuente de energía, solo que distinta. Eso es trabajo para un método en Car, sobrescrito en ElectricCar:

# en Car
def energy(self):
return "gasolina"
# en ElectricCar
def energy(self):
return "electricidad"
for c in fleet:
print(f"{c.brand}: {c.energy()}")
Salida
Skoda: gasolina
Tesla: electricidad
Dacia: gasolina
Nissan: electricidad

Mañana llega un coche híbrido. Con el método, escribes una clase nueva. Con las comprobaciones, buscas cada if del programa.

La regla:

  • Todo objeto lo hace, cada uno a su manera: un método en la clase padre, y sobrescritura.
  • Solo algunos objetos pueden hacerlo (cargar, pedalear): una comprobación de tipo.

Pruébalo

¿Qué falta?

for c in fleet:
    if (c, ElectricCar):
        c.charge()

En resumen

Los cuatro pilares de la POO

Con esta lección has visto todas las ideas de los módulos «Clases y objetos» y «Herencia y polimorfismo». La programación orientada a objetos se apoya en cuatro de ellas. Ya las usas todas:

  • Encapsulamiento. Los datos del objeto están ocultos, y desde fuera llegas a ellos solo a través de métodos que vigilan las reglas. Account guarda el saldo con un guion bajo y ofrece solo deposit() y withdraw(). Encapsulamiento
  • Herencia. Lo común está en un solo lugar, la clase padre, y la clase hija añade lo suyo. Manager es un Employee con un bono. Herencia
  • Polimorfismo. Una sola llamada, un comportamiento distinto según el objeto. e.monthly_pay() en un solo bucle calcula el pago de un empleado, un gerente y un becario. Polimorfismo
  • Abstracción. Dices qué tiene que saber hacer el objeto, y cada clase decide cómo. El abstracto Shape promete un área, el contrato Payable promete un importe a pagar. Clases abstractas, Interfaces

Cómo trabajan las cuatro juntas en un solo programa, cuándo duele cada una y qué se suele confundir está en la última lección del módulo: Principios de la POO.

Ejercicio

¿Qué es en realidad?

Resuélvelas en tu editor.

1. Aparcamiento. Una lista de coches normales y familiares (FamilyCar hereda de SimpleCar). Para cada uno, imprime su tipo. Cuidado con el orden de las comprobaciones.

Salida
Esto es un SimpleCar
Esto es un FamilyCar
Esto es un SimpleCar

2. Bailes. Una lista de pasos normales y de breakdance. Para los pasos de breakdance, imprime también la complejidad. Solo BreakDanceMove la tiene, así que pregunta por ella después de la comprobación.

Salida
moonwalk: DanceMove
windmill: BreakDanceMove, complejidad 8
salsa: DanceMove

3. Flota. Toma Car y ElectricCar del ejercicio de la lección «Herencia». En una lista, pon un «Skoda» con 40 l, un «Tesla» con eficiencia 5, un «Dacia» con 5 l y un «Nissan» con eficiencia 4. Cada uno recorre 100 km: los eléctricos con drive_electric, los demás con drive. Después imprime los kilómetros y el combustible o la batería.

Salida
Skoda: 100 km, combustible 30 l
Tesla: 100 km, batería 80%
El combustible no alcanza para 100 km
Dacia: 0 km, combustible 5 l
Nissan: 100 km, batería 75%

4. Zoológico. Una clase abstracta Animal con sound(), y clases hijas Cat, Dog y Fish. Solo Fish implementa Swimmable con swim(). En un bucle sobre los animales, llama al sonido de cada uno, y para los que saben nadar, llama también a swim().

Salida
¡Miau, miau!
¡Guau, guau!
El pez se calla.
El pez nada.

Más tareas

  • Pedales. En el ejercicio de la lección «Clases abstractas», garage[0].pedal() detenía el programa, porque ahí no hay una bicicleta. Arréglalo con una comprobación de tipo: en el bucle sobre los vehículos, todo vehículo arranca, y solo la bicicleta además pedalea.
  • Pagos por tipo. De la lección «Interfaces»: la lista payments guarda empleados y facturas. Imprime por separado el total de salarios y el total de facturas. Para Juan, María y las dos facturas: 5500 € en salarios y 2140 € en facturas.

Test

Ponte a prueba

1. isinstance(c, ElectricCar) es verdadero. ¿Cómo cargas el coche?
2. c apunta a un ElectricCar. ¿Qué da isinstance(c, Car)?
3. Compruebas si un coche es un Car o un ElectricCar. ¿En qué orden?
4. El programa tiene muchos if del tipo «si es un ElectricCar, haz una cosa, si no, haz otra». ¿Qué te dice eso?
5. c apunta a un coche normal. ¿Qué pasa con c.charge(), sin ninguna comprobación antes?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·