img

Что такое __init__ в Python?

21 ноября
20:00
Бесплатный вебинар
Введение в Docker
Ведущий — Филипп Игнатенко.
Руководитель центра разработки
Записаться
img
img

 

Хотите работать с объектно-ориентированным проектированием в Python? Начните уже сегодня, изучив метод Python под названием __init__.

В этом руководстве мы пробежимся по основам классов и объектов Python, после чего посмотрим, что же такое метод __init__.

После прочтения вы сможете ответить на такие вопросы, как:

  • Что такое переменные экземпляра класса и атрибуты класса?
  • Каким образом метод __init__ инициализирует атрибуты класса?
  • Как можно установить для атрибутов значения по умолчанию?
  • Как можно использовать методы класса в качестве конструкторов для создания объектов?

Давайте начнем. 

Классы и объекты Python

Классы – это основа объектно-ориентированного программирования в Python. Мы можем создать класс и определить атрибуты и методы для того, чтобы связать данные и соответствующие функции

После того, как вы создадите класс, вы можете использовать его как макет (или шаблон) для создания объектов (экземпляров класса).

Время примеров! Давайте создадим класс Employee, где у каждого объекта класса есть следующие атрибуты:

  • full_name: полное имя сотрудника в формате firstName lastName
  • emp_id: идентификатор сотрудника
  • department: отдел, в котором работает сотрудник
  • experience: опыт, который имеет сотрудник (количество лет)

Что это значит?

Каждый отдельный сотрудник – это экземпляр или объект класса Employee, и у каждого объекта есть свои значения для атрибутов full_nameemp_iddepartment и experience

Эти атрибуты также называются переменными экземпляра. Дальше мы будем использовать оба термина (атрибуты и переменные экземпляра), так как они взаимозаменяемы. 

Атрибуты мы добавим чуть позже. А пока мы создадим класс Employee:

class Employee:
    pass

Ключевое слово pass (которое мы используем в качестве заполнителя) помогает избежать возникновения ошибок при запуске сценария. 

Несмотря на то, что такая версия класса Employee мало чем может нам помочь, она все же является допустимым классом. Итак, теперь мы можем создавать объекты класса Employee:

employee_1 = Employee()

print(employee_1)
#Output: <__main__.Employee object at 0x00FEE7F0>

Кроме того, мы можем добавить атрибуты и инициализировать их значения:

employee_1.full_name = 'Amy Bell'
employee_1.department = 'HR'

Однако при таком подходе довольно часто возникают ошибки, и по сути он считается неэффективным. Кроме того, в данном случае вы не сможете использовать класс в качестве шаблона для создания объектов. И вот здесь на помощь приходит метод __init__.

Роль метода __init__ в классе Python

Когда мы создаем экземпляр класса, у нас должна быть возможность инициализировать переменные этого экземпляра, и в этом нам помогает метод __init__. Метод __init__ вызывается каждый раз, когда создается новый объект класса, для того, чтобы инициализировать значения переменных экземпляра. 

Если вы когда-нибудь работали с таким языком, как С++, то вы заметите, что метод __init__ работает по аналогии с конструкторами.

Определение метода __init__

Давайте добавим метод __init__ в класс Employee:

class Employee:
    def __init__(self, full_name,emp_id,department,experience):
        self.full_name = full_name
        self.emp_id = emp_id
        self.department = department
        self.experience = experience

Параметр self - это экземпляр класса, а self.attribute присваивает атрибутам экземпляра класса значения, указанные справа. 

И теперь мы можем создавать вот такие объекты:

employee_2 = Employee('Bella Joy','M007','Marketing',3)
print(employee_2)
# Output: <__main__.Employee object at 0x017F88B0>

Когда мы попытаемся распечатать объекты, которые обозначают сотрудников, мы не получим никакой полезной информации, кроме класса, к которому они принадлежат. Давайте добавим метод  __repr__, который будет задавать способ вывода информации для класса:

def __repr__(self):
        return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

Добавим метод __repr__ в класс Employee и получим:

class Employee:
    def __init__(self, full_name,emp_id,department,experience):
        self.full_name = full_name
        self.emp_id = emp_id
        self.department = department
        self.experience = experience
    
     def __repr__(self):
        return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

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

print(employee_2)
# Output: Bella Joy,M007 from Marketing with 3 years of experience.

Некоторые соглашения

Прежде чем двигаться дальше, обратите внимание на несколько замечаний:

  • Мы использовали self в качестве первого параметра в методе __init__, чтобы сделать ссылку на сам экземпляр класса, а для инициализации различных атрибутов мы использовали self.attribute_name. Рекомендуется использовать именно параметр self (впрочем, вы можете использовать любое другое имя).  
  • Когда мы определяли метод __init__, мы устанавливали имена параметров так, чтобы они соответствовали именам атрибутов. При таком подходе читать код гораздо проще. 

Как добавить значения по умолчанию для атрибутов

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

Давайте попробует создать экземпляр класса Employee, но при этом мы не будем передавать ему значение для атрибута experience:

employee_3 = Employee('Jake Lee','E001','Engineering')

Вы получите ошибку:

Traceback (most recent call last):
  File "main.py", line 22, in <module>
    employee_3 = Employee('Jake Lee','E001','Engineering')
TypeError: __init__() missing 1 required positional argument: 'experience'

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

В данном случае мы указали для атрибута experience значение по умолчанию 0:

class Employee:
    def __init__(self, full_name,emp_id,department,experience=0):
        self.full_name = full_name
        self.emp_id = emp_id
        self.department = department
        self.experience = experience
    
     def __repr__(self):
        return f"{self.full_name},{self.emp_id} from {self.department} with {self.experience} years of experience."

Мы смогли создать объект employee_3, не указывая значение для атрибута experience; значение по умолчанию для этого атрибута – 0.

employee_3 = Employee('Jake Lee','E001','Engineering')
print(employee_3.experience)
# Output: 0

Альтернативные конструкторы классов, которые используют методы класса

Пока мы только разобрали, как можно определить метод __init__ и как можно установить значения по умолчанию для атрибутов (если это необходимо). Кроме того, мы знаем, что мы должны передать значения в конструктор для всех необходимых атрибутов. 

Однако бывают такие ситуации, когда значения этих переменных экземпляра класса (или атрибутов) могут иметь совсем иную структуру данных, например, кортеж, словарь или строка JSON.

Так что же нам делать?

Давайте приведем пример. Представим, что у нас есть значения для переменных экземпляра класса, но они хранятся в словаре Python:

dict_fanny = {'name':'Fanny Walker','id':'H203','dept':'HR','exp':2}

Мы можем обратиться к словарю и получить все атрибуты следующим образом:

name = dict_fanny['name']
id = dict_fanny['id']
dept = dict_fanny['dept']
exp = dict_fanny['exp']

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

employee_4 = Employee(name, id, dept, exp)
print(employee_4)
# Output: Fanny Walker,H203 from HR with 2 years of experience.

Не забывайте, что вы должны проделать все это для каждого нового объекта, который вы создаете. Конечно, этот подход нельзя назвать эффективным, и мы безусловно можем сделать лучше. Но как?

В Python в качестве конструкторов для создания объектов класса мы можем использовать методы класса. Для того, чтобы создать метод класса, воспользуемся декоратором @classmethod

Давайте определим метод, который проанализирует словарь, получит значения для переменных экземпляра класса и воспользуется ими для того, чтобы создать объекты класса Employee.

    @classmethod
    def from_dict(cls,data_dict):
        full_name = data_dict['name']
        emp_id = data_dict['id']
        department = data_dict['dept']
        experience = data_dict['exp']
        return cls(full_name, emp_id, department, experience)

Теперь, когда нам нужно будет создать объекты с помощью данных, которые хранятся в словаре, мы можем использовать метод класса from_dict().

? Обратите внимание, что в методе класса мы используем не self, а cls. Здесь все по аналогии: параметр self используется для ссылки на экземпляр класса, а cls – для ссылки на класс. Кроме того, помните о том, что методы класса привязаны к классу, а не к объектам.

Именно поэтому, когда для создания объектов мы вызываем метод класса from_dict(), мы делаем это в классе Employee:

И теперь, если у нас есть словарь для каждого из n сотрудников, то для того, чтобы создать экземпляры класса, мы можем воспользоваться методом класса from_dict() в качестве конструктора. При этом нам не нужно будет извлекать из словаря значения для переменных экземпляра. 

Замечание касательно переменных класса

Здесь мы определили метод класса, который привязан к классу, а не к отдельным экземплярам. Так же как у нас могут быть методы класса, у нас могут быть и переменные класса. 

Подобно методам класса, переменные класса также привязаны к классу, а не к экземпляру. Когда атрибут принимает некое фиксированное значение (не зависимо от экземпляра класса), мы можем определить его как переменную класса. 

Часто задаваемые вопросы

1. Для чего нужен метод __init__ в Python?

Определив в классе метод __init__, вы сможете инициализировать атрибуты и переменные класса для всех экземпляров класса. Метод __init__ необходимо вызывать каждый раз, когда вы создаете новый экземпляр. 

2. Может ли быть несколько методов __init__ в классе Python?

В чем состоит цель наличия нескольких методов __init__? Возможность иметь несколько конструкторов для создания экземпляров класса. Однако вы не можете определить несколько методов __init__. Если вы попытаетесь определить несколько методов __init__, то каждый последующий будет переопределять предыдущий. И тем не менее, вы можете воспользоваться декоратором @classmethod для того, чтобы определить методы класса. Эти методы класса можно использовать в качестве конструкторов для создания экземпляров класса. 

3. Что будет, если вы не определите метод __init__ в классе?

Если вы не определите метод __init__, вы в любом случае сможете создавать экземпляры класса. Но в таком случае вам придется самостоятельно добавлять переменные экземпляров и присваивать им значения. Вы не сможете передать значения для переменных экземпляра через конструктор. Это не только может привести к возникновению ошибок, но и в принципе противоречит цели наличия классов как шаблонов, с помощью которых мы можем создавать экземпляры классов. 

4. Можно ли установить значения по умолчанию для аргументов в методе __init__?

Да, при определении метода __init__ вы можете указать значения по умолчанию для одного или нескольких атрибутов. Таким образом, вы можете сделать эти атрибуты необязательными. В случае, если вы не передадите значение для этих атрибутов в конструкторе, то они примут соответствующие значения по умолчанию.

5. Можно ли менять значение атрибута вне метода __init__?

Да, вы всегда можете поменять значение атрибута вне метода __init__. Кроме того, после того, как вы создадите отдельный экземпляр, вы можете динамически добавлять в нему новые атрибуты. 

Заключение

В этом руководстве мы разобрали, как можно использовать метод __init__ для инициализации значений переменных экземпляра класса. Но несмотря на то, что это просто, это может оказаться довольно рутинно, так как вы без конца будете повторять одни и те же действия, особенно если у вас много атрибутов. 

Если вам интересно, то вы можете изучить, что такое классы данных (dataclasses). В Python 3.7 и более поздних версиях для создания классов данных, в которых хранятся данные, вы можете использовать модуль dataclasses. Его можно использовать как дополнение к методу __init__ и других часто используемых методов; у этого модуля есть большое количество интересных функций: подсказки при вводе кода, задание более сложных значений по умолчанию и оптимизация. 

Ссылка
скопирована
Получите бесплатные уроки на наших курсах
Все курсы
Программирование
Скидка 25%
Python Advanced. Продвинутый курс
Освойте асинхронное и метапрограммирование, изучите аннотацию типов и напишите собственное приложение на FastAPI. Улучшите свои навыки Python, чтобы совершить быстрый рост вашего грейда до уровня middle.
Получи бесплатный
вводный урок!
Пожалуйста, укажите корректный e-mail
отправили вводный урок на твой e-mail!
Получи все материалы в telegram и ускорь обучение!
img
Еще по теме:
img
Гипервизор - это программное обеспечение для виртуализации, используемое для создания и запуска виртуальных машин (ВМ). Гипервиз
img
Виртуализация серверов позволяет запускать несколько виртуальных машин на одном физическом сервере. Запуск виртуальных машин (ВМ
img
Сегодня мы рассмотрим, как настроить и использовать PHP в проекте. Но прежде чем начать, нужно понять, что такое PHP. Что такое
img
Как разработчик, вы знаете, что HTML расшифровывается как HyperText Markup Language (язык разметки гипертекста). HTML — это язык
img
Бесконечные споры вокруг искусственного интеллекта приводят к путанице. Существует много терминов, которые кажутся похожими, но
img
SVG расшифровывается как масштабируемая векторная графика. Это веб-дружелюбный векторный формат файлов, используемый для отображ
21 ноября
20:00
Бесплатный вебинар
Введение в Docker