Python · Синтаксис · Початковий рівень

Основи ООП

11 завдань

Практичні завдання з основ ООП в Python: створення класів та об'єктів, атрибути, методи, __init__, зміна стану об'єкта та просте моделювання сутностей.

Класи й об'єкти — ідея

#
Клас — це шаблон. Об'єкт — це те, що ви будуєте за цим шаблоном. Один і той самий клас може породити тисячі об'єктів, кожен із власними незалежними даними. До класів, якщо потрібно відстежувати кілька банківських рахунків, доводилося б використовувати окремі змінні для кожного балансу. З класом визначаємо структуру один раз і створюємо скільки завгодно екземплярів — кожен зберігає свій власний стан. ```python class BankAccount: def __init__(self, balance): self.balance = balance acc1 = BankAccount(100) acc2 = BankAccount(500) print(acc1.balance) # 100 print(acc2.balance) # 500 ``` acc1 і acc2 — незалежні об'єкти. Зміна acc1.balance не впливає на acc2. Класи об'єднують дві речі: дані (атрибути) і поведінку (методи). Метод — це просто функція, визначена всередині класу. Саме таке об'єднання і є головною ідеєю об'єктно-орієнтованого програмування: зберігайте дані і код, що з ними працює, в одному місці.

__init__ і self

#
__init__ — спеціальний метод, який Python викликає автоматично при створенні нового об'єкта. Він налаштовує початковий стан екземпляра. ```python class Rectangle: def __init__(self, width, height): self.width = width self.height = height r = Rectangle(3, 4) print(r.width, r.height) # 3 4 ``` self — це посилання на об'єкт, що створюється. Коли ви пишете self.width = width, ви зберігаєте значення на самому об'єкті, а не в локальній змінній, яка зникне після завершення __init__. Кожен атрибут, який має пам'ятати об'єкт, має бути присвоєний через self. self завжди є першим параметром кожного методу, але ви ніколи не передаєте його вручну. Python передає його автоматично. Коли ви викликаєте r.area(), Python перетворює це на Rectangle.area(r) — r стає self всередині методу. ```python class Rectangle: def __init__(self, width, height): self.width = width self.height = height def area(self): return self.width * self.height r = Rectangle(3, 4) print(r.area()) # 12 ``` Метод використовує self.width і self.height — значення, що були збережені під час __init__. Через self метод має доступ до всього, що об'єкт знає про себе.

Поширені патерни класів

#
Метод, що змінює стан об'єкта — нічого не повертає, лише змінює атрибут: ```python class Counter: def __init__(self, value): self.value = value def increment(self): self.value += 1 c = Counter(0) c.increment() c.increment() print(c.value) # 2 ``` Метод, що перевіряє умову і повертає булеве значення: ```python class Person: def __init__(self, name, age): self.name = name self.age = age def is_adult(self): return self.age >= 18 ``` Метод, що обчислює щось на основі атрибутів і повертає значення: ```python class Temperature: def __init__(self, celsius): self.celsius = celsius def to_fahrenheit(self): return self.celsius * 9 / 5 + 32 ``` Метод, що одночасно змінює стан і перевіряє вхідні дані: ```python class BankAccount: def __init__(self, balance): self.balance = balance def deposit(self, amount): self.balance += amount ``` Патерн завжди однаковий: звертаємося до потрібного через self, обчислюємо або змінюємо, повертаємо лише тоді, коли є змістовне значення для повернення.

Швидкий довідник: основи ООП

#
**Синтаксис класу** ```python class НазваКласу: def __init__(self, параметр1, параметр2): self.атрибут1 = параметр1 # зберегти на об'єкті self.атрибут2 = параметр2 def метод(self): # self завжди перший return self.атрибут1 obj = НазваКласу(знач1, знач2) # створити екземпляр obj.атрибут1 # прочитати атрибут obj.метод() # викликати метод ``` **Правила `self`** | Правило | Чому | |---|---| | Кожен метод повинен мати `self` першим параметром | Python передає об'єкт автоматично | | Використовуйте `self.ім'я` для зберігання даних | просто `ім'я =` — це лише локальна змінна | | Використовуйте `self.ім'я` для читання в будь-якому методі | всі методи поділяють один об'єкт | | Ніколи не передавайте `self` вручну | `obj.метод()` а не `obj.метод(obj)` | **Типи методів** | Тип | Повертає | Приклад | |---|---|---| | Змінює стан | нічого (`None`) | `self.balance += amount` | | Перевіряє умову | `bool` | `return self.age >= 18` | | Обчислює значення | результат | `return self.w * self.h` | | Змішаний | залежить | валідація потім зміна | **Типові помилки** ```python # Забути self. — створюється локальна змінна що зникає: def __init__(self, name): name = name # неправильно — тільки локальна self.name = name # правильно # Забути self. у методі: def area(self): return width * height # NameError — має бути self.width return self.width * self.height # правильно # Пропустити self у сигнатурі: def area(): # неправильно — Python передає obj першим аргументом def area(self): # правильно ```
01

Клас User з ім’ям.

#

Створіть клас User з атрибутом name . Створіть об’єкт цього класу та виведіть його ім’я.

class User:
    pass


u = User("Alex")
print(u.name)
Рішення
class User:
    def __init__(self, name):
        self.name = name


u = User("Alex")
print(u.name)

# або можна створити метод, який повертає ім’я
class User:
    def __init__(self, name):
        self.name = name

    def get_name(self):
        return self.name
02

Клас Car: бренд і рік.

#

Створіть клас Car з атрибутами brand і year . Створіть об’єкт і виведіть рядок: "Car: Toyota, 2020"

class Car:
    pass
Рішення
class Car:
    def __init__(self, brand, year):
        self.brand = brand
        self.year = year


c = Car("Toyota", 2020)
print(f"Car: {c.brand}, {c.year}")

# або

class Car:
    def __init__(self, brand, year):
        self.brand = brand
        self.year = year
    def __str__(self):
        return f"Car: {self.brand}, {self.year}"


c = Car("Toyota", 2020)
print(c)
03

Лічильник з increment.

#

Створіть клас Counter з атрибутом value . Додайте метод increment , який збільшує значення на 1.

class Counter:
    pass


c = Counter(0)
c.increment()
print(c.value)
Рішення
class Counter:
    def __init__(self, value):
        self.value = value

    def increment(self):
        self.value += 1


c = Counter(0)
c.increment()
print(c.value)

# або так можна: increment може повертати нове значення
class Counter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1
        return self.value
04

Площа прямокутника.

#

Створіть клас Rectangle з атрибутами width і height . Додайте метод area , який повертає площу.

class Rectangle:
    pass


r = Rectangle(3, 4)
print(r.area())
Рішення
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height


r = Rectangle(3, 4)
print(r.area())

# або так можна: площу спочатку зберегти у змінну
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        result = self.width * self.height
        return result
05

Привітання методом класу.

#

Створіть клас Greeting з методом say_hello , який повертає рядок "Hello!" .

class Greeting:
    pass


g = Greeting()
print(g.say_hello())
Рішення
class Greeting:
    def say_hello(self):
        return "Hello!"


g = Greeting()
print(g.say_hello())
06

Перевірка повноліття.

#

Створіть клас Person з атрибутами name і age . Додайте метод is_adult , який повертає True , якщо вік 18 і більше.

class Person:
    pass


p = Person("John", 20)
print(p.is_adult())
Рішення
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def is_adult(self):
        return self.age >= 18


p = Person("John", 20)
print(p.is_adult())
07

Поповнення банківського рахунку.

#

Створіть клас BankAccount з атрибутом balance . Додайте метод deposit , який збільшує баланс.

class BankAccount:
    pass


acc = BankAccount(100)
acc.deposit(50)
print(acc.balance)
Рішення
class BankAccount:
    def __init__(self, balance):
        self.balance = balance

    def deposit(self, amount):
        self.balance += amount


acc = BankAccount(100)
acc.deposit(50)
print(acc.balance)

# або так можна: явно не дозволяти поповнювати рахунок від’ємною сумою
class BankAccount:
    def __init__(self, balance):
        self.balance = balance

    def deposit(self, amount):
        if amount > 0:
            self.balance += amount
08

Конвертація Celsius у Fahrenheit.

#

Створіть клас Temperature з атрибутом celsius . Додайте метод to_fahrenheit , який повертає температуру у градусах Фаренгейта. Формула така: celsius * 9 / 5 + 32.

class Temperature:
    pass


t = Temperature(0)
print(t.to_fahrenheit())
Рішення
class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius

    def to_fahrenheit(self):
        return self.celsius * 9 / 5 + 32


t = Temperature(0)
print(t.to_fahrenheit())
09

Довжина повідомлення методом.

#

Створіть клас Message з атрибутом text . Додайте метод length , який повертає довжину повідомлення.

class Message:
    pass


m = Message("Hello world")
print(m.length())
Рішення
class Message:
    def __init__(self, text):
        self.text = text

    def length(self):
        return len(self.text)


m = Message("Hello world")
print(m.length())
10

Таймер з додаванням секунд.

#

Створіть клас Timer з атрибутом seconds . Додайте метод add_time , який збільшує час на вказану кількість секунд.

class Timer:
    pass


t = Timer(10)
t.add_time(5)
print(t.seconds)
Рішення
class Timer:
    def __init__(self, seconds):
        self.seconds = seconds

    def add_time(self, extra):
        self.seconds += extra


t = Timer(10)
t.add_time(5)
print(t.seconds)
11

Клас Formatter для рядків.

#

Напишіть клас Formatter з атрибутом string у об’єкта. Під час створення екземпляра цього класу (об’єкта) туди передається рядок, який зберігається в цьому атрибуті string у об’єкта. У цього класу має бути кілька методів, які повинні повертати змінений варіант цього рядка відповідно до їх призначення: - метод trim повертає рядок, обрізаний до вказаної довжини в кінці з "[...]" (довжина цих доданих символів має бути врахована). - метод truncate повертає обрізаний рядок до вказаної довжини, але не "розриває слова", а обрізає його по пробілу, але не довше за вказану довжину. Цього достатньо. УВАГА: - початковий рядок у self.string завжди має залишатися початковим. - не створювати жодних інших атрибутів для результатів, лише повертати нові значення. Локальні змінні, звісно, можна. - можна створювати атрибути для службових даних, наприклад для рядка "[...]" Пам’ятайте, що методи — це просто особливі функції, куди в перший параметр self автоматично передається посилання на сам об’єкт, але усі інші правила у вигляді звичайних локальних змінних та іншого — як у звичайних функцій. Тобто не обов’язково всередині методу все навішувати на self.змінна, можна просто створювати локальні змінні як у звичайних функціях.

class Formatter():
    def __init__(self, string):
        pass
    
    def trim(self, length):     
        pass
        
    def truncate(self, length):
        pass

    
x = Formatter("Эта строка для примера просто используется тут и не имеет другого смысла.")
print(x.trim(15))      # Эта строка[...]
print(x.truncate(15))  # Эта строка для
print(x.string)  # Изначальная строка

y = Formatter("Ура у меня получилось!.")
print(y.trim(8))      # Ура[...]
print(y.truncate(3))  # Ура
print(y.string)  # Изначальная строка
Рішення
class Formatter():
    def __init__(self, string):
        self.string = string
        self.end = "[...]"
    
    def trim(self, length):     
        return self.string[: length - len(self.end)] + self.end
        
    def truncate(self, length):
        if self.string[length] == " ":
            return self.string[:length]
        
        return self.string[:length].rsplit(" ", 1)[0]
        
    
x = Formatter("Эта строка для примера просто используется тут и не имеет другого смысла.")
print(x.trim(15))      # Эта строка[...]
print(x.truncate(15))  # Эта строка для
print(x.string)  # Изначальная строка

y = Formatter("Ура у меня получилось!.")
print(y.trim(8))      # Ура[...]
print(y.truncate(3))  # Ура
print(y.string)  # Изначальная строка