__init__()
Магический метод __init__
- это метод класса, который отвечает за инициализацию объекта при создании. Метод класса __init__()
получает новый экземпляр класса, и именно в этот момент можно свободно настраивать и адаптировать его в соответствии с аргументами, которые были переданы при его создании.
Предположим, что нужно создать простую систему для отслеживания клиентов. Для этих целей необходим класс Client
, где у каждого клиента будет некоторая связанная информация, например, его имя и адрес электронной почты.
Таким образом, чтобы создать новый экземпляр Client
, необходимо будет передать имя и адрес электронной почты:
class Client: # ... # Создадим двух клиентов alice = Client("Alice", "alice@example.com") bob = Client("Bob", "bob@example.com")
Главное заключается в том, что и имя, и адрес электронной почты клиента остаются связанными с конкретным экземпляром объекта.
Рассмотрим подробнее строку, создающую клиента. Когда выполняется строка alice = Client("Alice", "alice@example.com")
, будет вызван магический метод __init__()
и ему будут предоставлены три фрагмента информации! Очевидно, что метод __init__
получит строку с именем "Alice"
и строку с адресом электронной почты "alice@example.com"
. Есть еще одна дополнительная информация, которую получит __init__
- это объект self
, который фактически инициализирует __init__
!.
Напомним, что __init__
должен инициализировать объект при создании, поэтому Python создаст пустой объект Client
, а затем передаст его в __init__
. Метод __init__
принимает этот пустой объект, и именно это позволяет прикрепить строки name
и email
в качестве атрибутов конкретного экземпляра.
Реализация класса Client
могла бы выглядеть примерно так:
class Client: def __init__(self, name, email): print(f"Создание клиента {name} с почтой {email}.") self.name = name self.email = email # Create two clients: alice = Client("Alice", "alice@example.com") # Создание клиента Alice с почтой alice@example.com. bob = Client("Bob", "bob@example.com") # Создание клиента Bob с почтой bob@example.com.
Обратите внимание, что внутри __init__
была запущена функция print()
, но метод __init__
явно не вызывался. Метод класса __init__
вызывался неявно как часть процесса создания и настройки каждого из экземпляров.
Метод __init___
сохраняет некоторые/все аргументы в качестве атрибутов экземпляра. Это делает эту информацию доступной позже, чтобы программа/другие методы могли получить к ней доступ.
Расширим класс Client
, чтобы он мог отправлять письма клиентам:
class Client: def __init__(self, name, email): print(f"Создание клиента {name} с почтой {email}.") self.name = name self.email = email def send_email(self, email_body): print(f"To: {self.name} <{self.email}>") print(email_body) # Create two clients: alice = Client("Alice", "alice@example.com") # Создание клиента Alice с почтой alice@example.com. alice.send_email("Нам нужно назначить встречу!") # To: Alice <alice@example.com> # Нам нужно назначить встречу!
__init__
и наследованиеКлассы Python поддерживают наследование от других классов, это означает, что можно повторно использовать часть поведения, которое они уже определяют. Когда класс наследуется от другого класса, необходимо быть внимательным при инициализации экземпляра подкласса. То есть нужно убедиться, что родительский класс(ы) смогут выполнить свою инициализацию! Если этого не произойдет, то в созданном экземпляре что-то будет отсутствовать...
Вернемся к классу Client
, который может быть подклассом Person
.
class Person: def __init__(self, name): self.name = name class Client(Person): def __init__(self, name, email): self.name = name self.email = email def send_email(self, email_body): print(f"To: {self.name} <{self.email}>") print(email_body)
Обратите внимание, что возникла проблема, которая заключается в дублировании self.name = name
в методах инициализации __init__
: в Person.__init__
и в Client.__init__
. Дублирования конечно-же следует избегать. Но чтобы исправить данную ситуацию, подкласс должен явно вызвать метод __init__
родительского класса, и это делается с помощью встроенной функции super()
.
Исправим реализацию Client.__init__()
, используя super()
:
class Person: # ... class Client(Person): def __init__(self, name, email): super().__init__(name) self.email = email # ...
Обратите внимание, на порядок инициализации, он может быть очень важным. Только после выполнения инициализации родительского класса нужно выполнять собственную инициализацию, и именно поэтому, после вызова super().__init__()
происходит присвоение атрибуту self.email
. Как правило, всегда рекомендуется начинать с инициализации родительского класса, и только потом подкласса.