Skip to content

Latest commit

 

History

History
906 lines (582 loc) · 21.1 KB

File metadata and controls

906 lines (582 loc) · 21.1 KB

Урок 4. Инициализатор __init__ и финализатор __del__

В предыдущем уроке мы научились создавать методы класса и разобрали важный параметр self, который позволяет методу работать с конкретным объектом.

Теперь пришло время познакомиться с двумя особыми методами Python, которые играют ключевую роль в жизненном цикле объекта:

  • __init__()инициализация объекта
  • __del__()финализация объекта

Эти методы относятся к так называемым магическим методам.


Магические методы Python

В Python существует набор специальных методов, которые автоматически вызываются интерпретатором в определённых ситуациях.

Такие методы принято называть магическими.

Их легко узнать по форме записи:

__имя_метода__

Например:

__init__
__del__
__str__
__repr__
__add__

Особенность этих методов:

  • они не вызываются напрямую
  • Python вызывает их автоматически

В этом уроке мы разберём два из них.

Метод Назначение
__init__ инициализация объекта
__del__ финализация (удаление объекта)

Проблема обычных классов

Возьмём класс Point из прошлого урока.

class Point:

    color = "red"
    circle = 2

    def set_coords(self, x, y):
        self.x = x
        self.y = y

    def get_coords(self):
        return (self.x, self.y)

Создадим объект.

pt = Point()

Проблема в том, что у объекта нет координат.

Если попытаться получить координаты:

pt.get_coords()

мы получим ошибку.

AttributeError: 'Point' object has no attribute 'x'

Почему?

Потому что координаты создаются только после вызова метода:

pt.set_coords(1, 2)

Это неудобно.

Логичнее, если объект сразу создаётся с нужными данными.

Именно для этого используется метод __init__.


Инициализатор __init__

Метод __init__ автоматически вызывается сразу после создания объекта.

Добавим его в класс.

class Point:

    color = "red"
    circle = 2

    def __init__(self):
        print("Вызов метода __init__")

        self.x = 0
        self.y = 0

Теперь создадим объект.

pt = Point()

Вывод:

Вызов метода __init__

Посмотрим атрибуты объекта.

print(pt.__dict__)

Результат:

{'x': 0, 'y': 0}

Что произошло?

Метод __init__ автоматически создал атрибуты x и y.


Как создаётся объект

Создание объекта происходит в несколько этапов.

Схема выглядит так:

Point()

    │
    │
    ▼

1. Выделение памяти для объекта
        │
        ▼
2. Вызов метода __new__
        │
        ▼
3. Создание объекта
        │
        ▼
4. Вызов метода __init__
        │
        ▼
5. Объект готов к использованию

Метод __new__ отвечает за создание объекта, а __init__ — за настройку уже созданного объекта.

Метод __new__ мы подробно разберём на следующем уроке.


Передача параметров в __init__

Чаще всего __init__ принимает аргументы.

Например:

class Point:

    def __init__(self, x, y):

        self.x = x
        self.y = y

Теперь при создании объекта нужно передать координаты.

pt = Point(1, 2)

Внутри метода происходит следующее:

self.x = 1
self.y = 2

Теперь объект содержит координаты.

print(pt.__dict__)

Результат:

{'x': 1, 'y': 2}

Почему используется self

Разберём внимательно строку:

self.x = x

Здесь происходит две вещи:

  1. создаётся атрибут объекта x

  2. в него записывается значение аргумента

То есть self — это ссылка на конкретный объект.


Имена параметров

Иногда можно запутаться в этой записи.

Например:

def __init__(self, a, b):

    self.x = a
    self.y = b

Это абсолютно корректно.

Параметры функции могут называться как угодно.

Но хорошей практикой считается:

имя параметра = имя атрибута

Поэтому чаще пишут так:

def __init__(self, x, y):

    self.x = x
    self.y = y

Это делает код более читаемым.


Значения по умолчанию

Поскольку __init__ — это обычная функция, в ней можно использовать значения по умолчанию.

class Point:

    def __init__(self, x=0, y=0):

        self.x = x
        self.y = y

Теперь возможны разные варианты создания объекта.

pt1 = Point()

pt2 = Point(10)

pt3 = Point(10, 20)

Результат:

pt1 → (0,0)
pt2 → (10,0)
pt3 → (10,20)

Пример более реалистичного класса

Создадим класс пользователя.

class User:

    def __init__(self, name, age):

        # имя пользователя
        self.name = name

        # возраст пользователя
        self.age = age

    def show_info(self):

        print("Имя:", self.name)
        print("Возраст:", self.age)

Создание объекта:

user = User("Иван", 25)

user.show_info()

Вывод:

Имя: Иван
Возраст: 25

Финализатор __del__

Теперь перейдём ко второму методу.

__del__(self)

Он вызывается перед уничтожением объекта.

Добавим его в класс.

class Point:

    def __init__(self, x, y):

        self.x = x
        self.y = y

    def __del__(self):

        print("Удаление объекта:", self)

Создадим объект.

pt = Point(1, 2)

Когда программа завершится, можно увидеть сообщение:

Удаление объекта: <__main__.Point object ...>

Это означает, что объект был уничтожен.


Когда удаляется объект

Python использует сборщик мусора (Garbage Collector).

Основная идея очень простая:

объект существует пока на него есть ссылки.

Схема:

pt ───► объект Point

Если удалить ссылку:

del pt

то объект становится ненужным.

И Python может удалить его из памяти.

Перед удалением вызывается __del__.


Пример работы __del__

class Test:

    def __init__(self):
        print("Объект создан")

    def __del__(self):
        print("Объект удалён")


t = Test()

del t

Вывод:

Объект создан
Объект удалён

Важное предупреждение про __del__

Метод __del__ используется очень редко.

Причины:

1. Невозможно точно предсказать момент вызова

Python может удалить объект не сразу.

Особенно если работает сборщик мусора.

2. Может не вызваться при завершении программы

Если программа завершается аварийно.

3. Может привести к сложным ошибкам

Например при циклических ссылках.

Поэтому в реальных проектах __del__ почти не используют.

Гораздо чаще применяются:

  • контекстные менеджеры (with)
  • методы закрытия ресурсов (close())

Когда __del__ может быть полезен

Иногда __del__ используют для:

  • логирования удаления объекта
  • отладки
  • освобождения ресурсов

Пример:

class FileLogger:

    def __init__(self, filename):

        self.filename = filename
        print("Открыт файл:", filename)

    def __del__(self):

        print("Закрытие файла:", self.filename)

Но даже в таких случаях чаще используют контекстные менеджеры.


Итог

В этом уроке мы разобрали два важных магических метода.

__init__

Используется для инициализации объекта.

Он:

  • вызывается автоматически
  • создаёт атрибуты объекта
  • принимает аргументы

__del__

Вызывается перед уничтожением объекта.

Но используется очень редко, потому что:

  • момент вызова трудно предсказать
  • сборщик мусора работает автоматически

Вопросы

  1. Что такое магические методы?
  2. Когда вызывается метод __init__?
  3. Что обычно происходит внутри __init__?
  4. Почему self обязательно используется в __init__?
  5. Можно ли передавать параметры в __init__?
  6. Что происходит если не передать аргументы в __init__, когда они обязательны?
  7. Когда вызывается метод __del__?
  8. Почему __del__ редко используется в реальных проектах?
  9. Что такое сборщик мусора Python?
  10. Сколько этапов происходит при создании объекта?

Задачи

Задача 1

Объявите класс Money так, чтобы объекты этого класса можно было создавать следующим образом:

my_money = Money(100)
your_money = Money(1000)

Число, передаваемое при создании объекта, должно сохраняться в локальном атрибуте объекта money.

После создания объектов выведите значения денег:

print(my_money.money)
print(your_money.money)

Ожидаемый вывод:

100
1000

Задача 2

Объявите класс Point так, чтобы объекты можно было создавать командами:

p1 = Point(10, 20)
p2 = Point(12, 5, 'red')

Аргументы:

x — координата
y — координата
color — цвет точки

Если цвет не указан, он должен автоматически принимать значение:

black

Создайте 1000 объектов этого класса со следующими координатами:

(1,1), (3,3), (5,5), (7,7) ...

Каждая следующая точка должна увеличиваться на 2.

Все объекты нужно сохранить в список:

points

Для второго объекта списка задайте цвет "yellow".


Задача 3

Объявите три класса:

Line
Rect
Ellipse

Объекты этих классов должны создаваться следующим образом:

g1 = Line(a, b, c, d)
g2 = Rect(a, b, c, d)
g3 = Ellipse(a, b, c, d)

Передаваемые параметры:

a, b — координаты первой точки
c, d — координаты второй точки

В каждом объекте нужно создать атрибуты:

sp = (a, b)
ep = (c, d)

Создайте 213 объектов случайных классов:

Line
Rect
Ellipse

Координаты также должны генерироваться случайно в диапазоне от 0 до 100.

Все объекты сохраните в список:

elements

После этого обнулите координаты только у объектов класса Line.


Задача 4

Создайте класс User.

При создании объекта должны задаваться:

name

В конструкторе (__init__) должно выводиться сообщение:

Пользователь <name> создан

В финализаторе (__del__) должно выводиться сообщение:

Пользователь <name> удалён

Затем:

  1. Создайте список users
  2. Добавьте в него 3 объекта User
  3. Удалите один объект из списка
  4. Принудительно удалите переменную

Задача 5

Объявите класс TriangleChecker.

Объекты должны создаваться так:

tr = TriangleChecker(a, b, c)

Где:

a, b, c — длины сторон треугольника

В классе должен быть метод:

is_triangle()

Метод должен возвращать:

1 — если хотя бы одна сторона не число
или меньше либо равна нулю

2 — если такие стороны не могут образовать треугольник

3 — если стороны образуют треугольник

Проверки выполняются строго в указанном порядке.

Затем прочитайте значения:

a, b, c = map(int, input().split())

Создайте объект tr и выведите результат метода is_triangle().


Задача 6

Создайте три класса:

  • CPU
  • Memory
  • MotherBoard

Объекты создаются следующим образом:

cpu = CPU(name, frequency)
mem = Memory(name, volume)
mb = MotherBoard(name, cpu, mem1, mem2, ...)

Каждый объект должен хранить:

CPU

name
fr

Memory

name
volume

MotherBoard

name
cpu
mem_slots
total_mem_slots = 4

Список mem_slots должен содержать не более 4 объектов Memory. Если при создании передать большее количество оперативной памяти, то mem_slots должен будет содержать только первые 4 из них.

Класс MotherBoard должен иметь метод get_config() который возвращает список строк:

Материнская плата: <name>
Центральный процессор: <cpu_name>, <frequency>
Слотов памяти: 4
Память: name1 - volume1; name2 - volume2

Создайте материнскую плату с 1 CPU и 2 планками памяти

Выведите результат get_info() на экран.

Создайте материнскую плату с 1 CPU и 5 планками памяти.

Выведите результат get_info() на экран.


Задача 7

В программировании часто используются специальные структуры данных для хранения и обработки информации. Одна из самых базовых структур — связный список.

Односвязный список (Singly Linked List) — это последовательность объектов, где каждый объект хранит:

  1. данные
  2. ссылку на следующий объект

В отличие от обычного списка Python (list), элементы связного списка не хранятся в одной структуре. Каждый элемент существует как отдельный объект, который знает только о следующем элементе.

Схематично это выглядит так:

head_obj
   |
   v
+---------+      +---------+      +---------+      +---------+
| data    | ---> | data    | ---> | data    | ---> | data    |
| next_obj|      | next_obj|      | next_obj|      | next_obj|
+---------+      +---------+      +---------+      +---------+
      |                |                |               |
      v                v                v               v
    объект2         объект3          объект4          None
  • Первый объект называется головой списка (head).
  • Каждый объект хранит ссылку на следующий объект.
  • Последний элемент содержит next_obj = None.

Условие Задачи:

Объявите в программе класс ListObject, объекты которого создаются командой:

obj = ListObject(data)

где data — строка, которая должна сохраняться внутри объекта.


Каждый объект класса ListObject должен иметь два локальных атрибута:

1. data строка, переданная при создании объекта.

2. next_obj ссылка на следующий объект списка.

Если следующий объект отсутствует, значение должно быть:

None

В классе ListObject необходимо реализовать метод:

link(self, obj)

Назначение метода:

соединить текущий объект self со следующим объектом obj.

После вызова метода:

self.next_obj → obj

Далее в программе необходимо сформировать односвязный список используя строки из следующего списка:

lst_in = [
    'Введение в ООП',
    '1 Концепция ООП простыми словами',
    '2 Классы и объекты. Атрибуты классов и объектов',
    '3 Методы класса и параметр `self`',
    '4 Инициализатор init и финализатор del'
]

Ваша задача:

  1. Для каждой строки необходимо создать объект класса ListObject.
  2. В атрибуте data каждого объекта должна храниться соответствующая строка.
  3. Все объекты должны быть последовательно соединены друг с другом через метод link().
  4. Переменная head_obj должна ссылаться на первый объект списка.

Предыдущий урок | Следующий урок