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}%")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
- fleet es una lista: 2 coches normales y 2 eléctricos.
- c apunta al Skoda: isinstance da «no». El cuerpo del if se salta.
- c apunta al Tesla: isinstance da «sí». No hace falta una variable nueva: c.charge() carga el mismo coche.
- c apunta al Dacia: isinstance da «no». El cuerpo del if se salta.
- c apunta al Nissan: isinstance da «sí». No hace falta una variable nueva: c.charge() carga el mismo coche.
- 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))Un coche eléctrico también es un coche, así que la primera comprobación es verdadera. La comprobación no pregunta «¿es exactamente un Car?», sino «¿se puede usar este objeto como un Car?». El coche normal d no es eléctrico, así que la última comprobación es falsa.
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"))TrueFalseTrueTrue Falsetype(c) is ElectricCarpregunta «¿es exactamente unElectricCar?». Para el coche eléctricotype(c) is CaresFalse, mientras queisinstance(c, Car)esTrue. Casi siempre quieresisinstance: 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 concharge().
Más precisamente, para curiosos
- La comprobación siempre es falsa para
None, eisinstancetambié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)))FalseTrue- La comprobación también funciona con contratos:
isinstance(a, Swimmable)pregunta si la clase del objeto ha aceptado la clase abstractaSwimmable.
Pruébalo
Encuentra el error
El programa cuenta los coches normales y los eléctricos. La flota tiene dos de cada uno, pero imprime:
Normales: 4, eléctricos: 0¿En qué línea está el error?
Elige la línea con el error.
Todo coche eléctrico también es un Car, así que la primera comprobación es verdadera para los cuatro coches. El programa nunca llega a la segunda. Comprueba primero el tipo más concreto: si es un ElectricCar, cuéntalo como eléctrico, si no, como normal.
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 Cardef energy(self): return "gasolina"
# en ElectricCardef energy(self): return "electricidad"for c in fleet: print(f"{c.brand}: {c.energy()}")Skoda: gasolinaTesla: electricidadDacia: gasolinaNissan: electricidadMañ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()isinstance: comprueba si c apunta a un ElectricCar. Si es así, c.charge() funciona.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.
Accountguarda el saldo con un guion bajo y ofrece solodeposit()ywithdraw(). Encapsulamiento - Herencia. Lo común está en un solo lugar, la clase padre, y la clase hija añade lo suyo.
Manageres unEmployeecon 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
Shapepromete 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.
Esto es un SimpleCarEsto es un FamilyCarEsto es un SimpleCar2. 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.
moonwalk: DanceMovewindmill: BreakDanceMove, complejidad 8salsa: DanceMove3. 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.
Skoda: 100 km, combustible 30 lTesla: 100 km, batería 80%El combustible no alcanza para 100 kmDacia: 0 km, combustible 5 lNissan: 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().
¡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
Lecciones relacionadas
Sección «Lecciones relacionadas»- Se basa en: Herencia · Polimorfismo · Clases abstractas · Interfaces como clases abstractas
- Continúa en: Principios de la POO · Lanzar excepciones y crear las tuyas · Códigos hash y funciones hash · dataclass, match y patrones
- Ejercicios sobre el tema: Herencia y polimorfismo