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

Конструктори и self

Проблем

Кафе за 0 €

Създаваш продукт, както в миналия урок, и попълваш полетата ред по ред. Един ред си забравил:

coffee = Product()
coffee.name = "Кафе"
coffee.quantity = 2
print(f"{coffee.name}: {coffee.price * coffee.quantity} €")
Изход
Кафе: 0.0 €

Кафето струва 0 €. Python не се оплаква: цената си има стойност, 0.0 от __init__. Колкото повече полета има класът, толкова по-лесно е да пропуснеш едно. А продукт без цена не трябва изобщо да може да съществува.

Идеята

Конструктор: без тези данни няма обект

Класът може да каже какво е нужно, за да се създаде обект от него. За това служи конструкторът: специален метод, който се изпълнява при Product(...). В Python това е __init__, който вече видя, само че сега получава параметри.

shop.py
class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
coffee = Product("Кафе", 7.50, 2)
print(f"{coffee.name}: {coffee.price * coffee.quantity} €")
Изход
Кафе: 15.0 €
  • Конструкторът се казва __init__ (две долни черти отпред и две отзад), във всеки клас.
  • Първият му параметър е self: обектът, който се създава. Python го подава сам.
  • Останалите параметри са данните, без които продукт няма. Те се подават в скобите: Product("Кафе", 7.50, 2).
  • Нищо не връща. Product(...) връща новия обект.

Сега няма как да забравиш цената. Ако подадеш два аргумента вместо три, програмата спира още на този ред:

coffee = Product("Кафе", 7.50)
Грешка
TypeError: Product.__init__() missing 1 required positional argument: 'quantity'

class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity
coffee = Product("Кафе", 7.50, 2)
print(f"{coffee.name}: {coffee.price * coffee.quantity} €")
Стъпка по стъпка, с думи
  1. Product(...) прави нов обект. Полета още няма: ще ги създаде __init__.
  2. Изпълнява се __init__. self е новият обект, а аргументите отиват в параметрите.
  3. self.name = name: от параметъра в ново поле на този обект.
  4. Същото за цената и бройката.
  5. __init__ свърши и параметрите изчезват. В coffee отива стрелка към готовия обект.
  6. Обектът излиза от Product(...) вече попълнен, затова цената не е 0.

Идеята

self: полето на този обект

Вътре в __init__ има две неща с име name: параметърът и полето. name е параметърът. Полето е self.name.

class Product:
def __init__(self, name, price, quantity):
self.name = name
self.price = price
self.quantity = quantity

self е обектът, който се създава в момента. self.name = name значи „в полето name на този обект запиши параметъра name“.

В Python self. е задължително. Без него name = name е просто променлива вътре в __init__, която изчезва, щом той свърши. Името self е договорка, а не ключова дума, но така го пишат всички.

Пробвай

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

Програмата трябва да отпечата „Чай: 3.2 €“. Вместо това спира с AttributeError: 'Product' object has no attribute 'name'. На кой ред е грешката?

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

Идеята

А Product() от миналия урок?

В миналия урок __init__ нямаше параметри освен self, затова Product() работеше. Сега __init__ иска три аргумента и без тях програмата спира:

empty = Product()
Грешка
TypeError: Product.__init__() missing 3 required positional arguments: 'name', 'price', and 'quantity'

Това е целта: продукт без име и цена вече не може да се създаде.

По-точно, за любопитните

Ако клас изобщо няма __init__, Product() пак работи: Python създава обект без нито едно поле. Затова полетата в Python винаги се създават в __init__.

Идеята

Бройка по подразбиране

Повечето продукти в количката са по един брой. Да пишеш 1 всеки път е досадно.

В Python класът има един __init__. Вместо втори конструктор параметърът получава стойност по подразбиране: ако не подадеш бройка, тя е 1.

class Product:
def __init__(self, name, price, quantity=1):
self.name = name
self.price = price
self.quantity = quantity

quantity=1 (без интервали около =) значи „ако бройка не е подадена, вземи 1“. Параметрите със стойност по подразбиране са последни. Ако след quantity=1 сложиш параметър без стойност, Python не пуска файла:

Грешка
SyntaxError: parameter without a default follows parameter with a default
По-точно, за любопитните

Втори __init__ в класа не прави втори конструктор. Той просто заменя първия, без предупреждение, и Product("Хляб", 1.20) спира с:

Грешка
TypeError: Product.__init__() missing 1 required positional argument: 'quantity'

Затова в Python има само един конструктор. Предимството: ако утре добавиш проверка на цената, тя е на едно място.

Пробвай

Кой конструктор?

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

class Product:
def __init__(self, name, price, quantity=1):
self.name = name
self.price = price
self.quantity = quantity
bread = Product("Хляб", 1.20)
milk = Product("Мляко", 2.10, 3)
print(bread.quantity, milk.quantity)
Отговор

Пробвай

Как се казва конструкторът?

Какво трябва да пише на празното място?

class Book:
    def (self, title, author):
        self.title = title
        self.author = author

Упражнение

Обекти, създадени цели

Реши задачите в редактора си. Всеки клас получава конструктор с параметри за всички полета.

1. Човек. Клас Person с име и възраст. Създай двама души и отпечатай данните им.

Изход
Мария, 34 г.
Петър, 19 г.

2. Животно. Клас Animal с вид и възраст. Създай котка на 3 и куче на 7 години.

Изход
Вид: котка, възраст: 3
Вид: куче, възраст: 7

3. Танцова стъпка. Клас DanceMove с име и продължителност в секунди. В урока „Поведение“ стъпката ще се научи да се „изпълнява“.

Изход
moonwalk: 5 сек.

Още задачи

  • Книга с конструктор. Препиши Book от миналия урок с конструктор __init__(self, title, author). Колко реда спести в кода под класа?
  • Продукт с бройка по подразбиране. Product с quantity=1, както в урока. Направи списък [Product("Хляб", 1.20, 2), Product("Кафе", 7.50)] и отпечатай името и бройката на всеки: „Хляб x2“, „Кафе x1“.
  • Резервация. Клас Booking с име на госта, брой нощувки и цена за нощувка. Отпечатай колко струва престоят: за 3 нощувки по 85 € това е 255 €.

Тест

Провери се

1. Кога се изпълнява __init__?
2. Как се казва конструкторът в Python?
3. В класа има само def __init__(self, name, price). Какво става с Product()?
4. В __init__ пише self.name = name. Кое е self.name?
5. Как класът дава бройка 1, когато при създаването не е подадена?

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