Какъв е всъщност?
Проблем
Само някои могат да се зареждат
Фирмата има автопарк от обикновени и електрически коли. Всяка вечер електрическите трябва да се заредят. ElectricCar наследява Car и добавя charge(), затова всички са в един списък:
fleet = [ Car("Шкода"), ElectricCar("Тесла", 35), Car("Дачия"), ElectricCar("Нисан", 60),]for c in fleet: c.charge()Програмата тръгва и спира още на първата кола, „Шкода“: AttributeError: 'Car' object has no attribute 'charge'. До електрическите не стига.
Обикновената кола наистина не може да се зареди. Трябва да питаш всяка кола: „ти електрическа ли си?“
Идеята
Питай обекта какъв е
for c in fleet: if isinstance(c, ElectricCar): c.charge() print(f"{c.brand}: заредена до {c.battery}%")Тесла: заредена до 100%Нисан: заредена до 100%isinstance(c, ElectricCar) пита обекта, към който сочи c: „ти ElectricCar ли си?“ и дава True или False. Това се казва проверка на типа.
Нова променлива не трябва. В Python променливите нямат тип, затова няма какво да се превръща: щом проверката е вярна, c сочи електрическа кола и c.charge() работи. Колите остават същите.
for c in fleet: if isinstance(c, ElectricCar): c.charge() print(f"{c.brand}: заредена до {c.battery}%")Стъпка по стъпка, с думи
- fleet е списък: 2 обикновени и 2 електрически коли.
- c сочи Шкода: isinstance дава „не“. Тялото на if се прескача.
- c сочи Тесла: isinstance дава „да“. Нова променлива не трябва: c.charge() зарежда същата кола.
- c сочи Дачия: isinstance дава „не“. Тялото на if се прескача.
- c сочи Нисан: isinstance дава „да“. Нова променлива не трябва: c.charge() зарежда същата кола.
- Колите не се променят. Проверката само пита обекта какъв е.
Пробвай
Кола ли е?
Какво ще отпечата програмата?
c = ElectricCar("Тесла", 35)d = Car("Шкода")print(isinstance(c, Car))print(isinstance(c, ElectricCar))print(isinstance(d, ElectricCar))Електрическата кола е и кола, затова първата проверка е вярна. Проверката не пита „точно Car ли е?“, а „може ли обектът да се ползва като Car?“. Обикновената кола d не е електрическа, затова последната проверка е грешна.
Идеята
Другите записи, които ще срещнеш
Освен isinstance ще срещнеш още два начина да питаш обекта:
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 ElectricCarпита „точноElectricCarли е?“. За електрическата колаtype(c) is CarеFalse, аisinstance(c, Car)еTrue. Почти винаги искашisinstance: тя приема и наследниците.hasattr(c, "charge")не пита за класа, а само дали обектът има такъв метод. Това е проверка по договор, както при интерфейсите: всеки клас сcharge()минава.
По-точно, за любопитните
- Проверката е винаги грешна за
None, аisinstanceприема и няколко класа наведнъж, в кръгли скоби: вярно е, ако обектът е от който и да е от тях.
print(isinstance(None, Car))print(isinstance(fleet[1], (Car, ElectricCar)))FalseTrue- Проверката работи и с договори:
isinstance(a, Swimmable)пита дали класът на обекта е приел абстрактния класSwimmable.
Пробвай
Намери грешката
Програмата брои обикновените и електрическите коли. В автопарка има две от всеки вид, а тя печата:
Обикновени: 4, електрически: 0На кой ред е грешката?
Избери реда с грешката.
Всяка електрическа кола е и Car, затова първата проверка е вярна за всички четири коли. До втората програмата не стига никога. Проверявай първо по-конкретния тип: ако е ElectricCar, брой електрическа, иначе обикновена.
Идеята
Проверка или метод?
Искаш да отпечаташ с какво се движи всяка кола. С проверка на типа става:
for c in fleet: if isinstance(c, ElectricCar): print(f"{c.brand}: ток") else: print(f"{c.brand}: бензин")Но това е заплатата с if по роля от урока за полиморфизма. Всяка кола има енергия, само че различна. Това е работа за метод в Car, презаписан в ElectricCar:
# в Cardef energy(self): return "бензин"
# в ElectricCardef energy(self): return "ток"for c in fleet: print(f"{c.brand}: {c.energy()}")Шкода: бензинТесла: токДачия: бензинНисан: токУтре идва хибридна кола. С метода пишеш нов клас. С проверките търсиш всеки if в програмата.
Правилото:
- Всеки обект го прави, но по свой начин: метод в родителя и презаписване.
- Само някои обекти го могат (зареждане, педали): проверка на типа.
Пробвай
Кое липсва?
for c in fleet:
if (c, ElectricCar):
c.charge()isinstance: проверява дали c сочи ElectricCar. Ако да, c.charge() работи.Накратко
Четирите стълба на ООП
С този урок модулите „Класове и обекти“ и „Наследяване и полиморфизъм“ са минати. Обектно-ориентираното програмиране стои на четири идеи. Вече ползваш всичките:
- Капсулация. Данните на обекта са скрити, а отвън се стига до тях само през методи, които пазят правилата.
Accountдържи баланса с долна черта и дава самоdeposit()иwithdraw(). Капсулация - Наследяване. Общото е на едно място, в родителя, а наследникът добавя своето.
ManagerеEmployeeс бонус. Наследяване - Полиморфизъм. Едно извикване, различно поведение според обекта.
e.monthly_pay()в един цикъл смята заплатата на служител, мениджър и стажант. Полиморфизъм - Абстракция. Казваш какво трябва да може обектът, а как решава всеки клас. Абстрактният
Shapeобещава лице, договорът Payable обещава сума за плащане. Абстрактни класове, Интерфейси
Упражнение
Какъв е всъщност?
Реши задачите в редактора си.
1. Паркинг. Списък с обикновени и семейни коли (FamilyCar наследява SimpleCar). За всяка отпечатай какъв е типът ѝ. Внимавай с реда на проверките.
Това е SimpleCarТова е FamilyCarТова е SimpleCar2. Танци. Списък с обикновени и брейк движения. За брейк движенията отпечатай и сложността. Тя е само в BreakDanceMove, затова я питай след проверката.
moonwalk: DanceMovewindmill: BreakDanceMove, сложност 8салса: DanceMove3. Автопарк. Вземи Car и ElectricCar от упражнението в урока „Наследяване“. В списък сложи „Шкода“ с 40 л, „Тесла“ с ефективност 5, „Дачия“ с 5 л и „Нисан“ с ефективност 4. Всяка да измине 100 км: електрическите с drive_electric, останалите с drive. После отпечатай километрите и горивото или батерията.
Шкода: 100 км, гориво 30 лТесла: 100 км, батерия 80%Горивото не стига за 100 кмДачия: 0 км, гориво 5 лНисан: 100 км, батерия 75%4. Зоопарк. Абстрактен клас Animal със sound(), наследници Cat, Dog и Fish. Само Fish реализира Swimmable с swim(). В цикъл през животните извикай звука на всяко, а на тези, които могат да плуват, и swim().
Мяу, мяу!Бау, бау!Рибата мълчи.Рибата плува.Още задачи
- Педали. В упражнението от урока „Абстрактни класове“
garage[0].pedal()спираше програмата, защото там няма велосипед. Поправи го с проверка на типа: в цикъла през превозните средства всяко потегля, а само велосипедът и върти педали. - Плащания по вид. От урока „Интерфейси“: в списъка payments има служители и фактури. Отпечатай отделно сумата за заплати и сумата за фактури. За Иван, Мария и двете фактури: 5500 € заплати и 2140 € фактури.
Тест