Към съдържанието
wannacode

Какъв е всъщност?

Проблем

Само някои могат да се зареждат

Фирмата има автопарк от обикновени и електрически коли. Всяка вечер електрическите трябва да се заредят. 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}%")
Стъпка по стъпка, с думи
  1. fleet е списък: 2 обикновени и 2 електрически коли.
  2. c сочи Шкода: isinstance дава „не“. Тялото на if се прескача.
  3. c сочи Тесла: isinstance дава „да“. Нова променлива не трябва: c.charge() зарежда същата кола.
  4. c сочи Дачия: isinstance дава „не“. Тялото на if се прескача.
  5. c сочи Нисан: isinstance дава „да“. Нова променлива не трябва: c.charge() зарежда същата кола.
  6. Колите не се променят. Проверката само пита обекта какъв е.

Пробвай

Кола ли е?

Какво ще отпечата програмата?

c = ElectricCar("Тесла", 35)
d = Car("Шкода")
print(isinstance(c, Car))
print(isinstance(c, ElectricCar))
print(isinstance(d, ElectricCar))
Отговор

Идеята

Другите записи, които ще срещнеш

Освен 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"))
Изход
True
False
True
True False
  • type(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)))
Изход
False
True
  • Проверката работи и с договори: isinstance(a, Swimmable) пита дали класът на обекта е приел абстрактния клас Swimmable.

Пробвай

Намери грешката

Програмата брои обикновените и електрическите коли. В автопарка има две от всеки вид, а тя печата:

Изход
Обикновени: 4, електрически: 0

На кой ред е грешката?

Избери реда с грешката.

Идеята

Проверка или метод?

Искаш да отпечаташ с какво се движи всяка кола. С проверка на типа става:

for c in fleet:
if isinstance(c, ElectricCar):
print(f"{c.brand}: ток")
else:
print(f"{c.brand}: бензин")

Но това е заплатата с if по роля от урока за полиморфизма. Всяка кола има енергия, само че различна. Това е работа за метод в Car, презаписан в ElectricCar:

# в Car
def energy(self):
return "бензин"
# в ElectricCar
def energy(self):
return "ток"
for c in fleet:
print(f"{c.brand}: {c.energy()}")
Изход
Шкода: бензин
Тесла: ток
Дачия: бензин
Нисан: ток

Утре идва хибридна кола. С метода пишеш нов клас. С проверките търсиш всеки if в програмата.

Правилото:

  • Всеки обект го прави, но по свой начин: метод в родителя и презаписване.
  • Само някои обекти го могат (зареждане, педали): проверка на типа.

Пробвай

Кое липсва?

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

Накратко

Четирите стълба на ООП

С този урок модулите „Класове и обекти“ и „Наследяване и полиморфизъм“ са минати. Обектно-ориентираното програмиране стои на четири идеи. Вече ползваш всичките:

  • Капсулация. Данните на обекта са скрити, а отвън се стига до тях само през методи, които пазят правилата. Account държи баланса с долна черта и дава само deposit() и withdraw(). Капсулация
  • Наследяване. Общото е на едно място, в родителя, а наследникът добавя своето. Manager е Employee с бонус. Наследяване
  • Полиморфизъм. Едно извикване, различно поведение според обекта. e.monthly_pay() в един цикъл смята заплатата на служител, мениджър и стажант. Полиморфизъм
  • Абстракция. Казваш какво трябва да може обектът, а как решава всеки клас. Абстрактният Shape обещава лице, договорът Payable обещава сума за плащане. Абстрактни класове, Интерфейси

Упражнение

Какъв е всъщност?

Реши задачите в редактора си.

1. Паркинг. Списък с обикновени и семейни коли (FamilyCar наследява SimpleCar). За всяка отпечатай какъв е типът ѝ. Внимавай с реда на проверките.

Изход
Това е SimpleCar
Това е FamilyCar
Това е SimpleCar

2. Танци. Списък с обикновени и брейк движения. За брейк движенията отпечатай и сложността. Тя е само в BreakDanceMove, затова я питай след проверката.

Изход
moonwalk: DanceMove
windmill: BreakDanceMove, сложност 8
салса: DanceMove

3. Автопарк. Вземи 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 € фактури.

Тест

Провери се

1. isinstance(c, ElectricCar) е вярно. Как зареждаш колата?
2. c сочи ElectricCar. Колко е isinstance(c, Car)?
3. Проверяваш дали колата е Car или ElectricCar. В какъв ред?
4. В програмата има много if-ове „ако е ElectricCar, прави едно, иначе друго“. Какво подсказва това?
5. c сочи обикновена кола. Какво става при c.charge(), без проверка преди това?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·