Что такое init в python простым языком
Перейти к содержимому

Что такое init в python простым языком

  • автор:

Инициализация и протоколы — Python: Введение в ООП

Теперь, когда вы знакомы со связанными методами, настало время рассказать про самый важный метод в Python: метод __init__ («dunder-init», «дандер инит»). Этот метод отвечает за инициализацию экземпляров класса после их создания.

На прошлых уроках Бобу — экземпляру класса Person , мы задавали имя уже после того, как сам объект был создан. Такое заполнение атрибутов объекта и выглядит громоздко и может приводить к разного рода ошибкам: объект может какое-то время находиться в «недозаполненном» (более общее название — «неконсистентном») состоянии. Инициализатор же позволяет получить уже полностью настроенный экземпляр.

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

class Person: def __init__(self, name): self.name = name bob = Person('Bob') bob.name # 'Bob' alice = Person('Alice') alice.name # 'Alice' 

Теперь нет нужды иметь в классе атрибут name = ‘Noname’ , ведь все объекты получают имена при инстанцировании!

Методы и протоколы

Вы заметили, что Python сам вызывает метод __init__ в нужный момент? Это же касается большинства dunder-методов: таковые вы только объявляете, но вручную не вызываете. Такое поведение часто называют протоколом (или поведением): класс реализует некий протокол, предоставляя нужные dunder-методы. А Python, в свою очередь, работает с объектом посредством протокола.

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

class Person: def __init__(self, name): self.name = name def __len__(self): return len(self.name) tom = Person('Thomas') len(tom) # 6 

Я объявил метод dunder-len, и объекты класса Person научились возвращать «свою длину» (да, пример надуманный, но суть отражает). Вызов функции len на самом деле приводит к вызову метода __len__ у переданного в аргументе объекта!

Протоколы и «утиная типизация»

Существует такой термин: «утиная типизация» («duck typing») — «Если что-то крякает как утка и плавает как утка, то возможно это утка и есть!». Данный термин, пусть и звучит как шутка, описывает важный принцип построения абстракций: коду практически всегда достаточно знать о значении, с которым этот код работает, что это значение обладает нужными свойствами. Все остальное — не существенно.

Если коду достаточно знать, что сущность умеет плавать и крякать, то код не должен проверять сущность на фактическую принадлежность к уткам. Это позволит коду работать с робо-утками, даже если все настоящие утки вымрут, ведь код работает с абстрактными утками!

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

6 рекомендаций по определению метода __init__

Основным краеугольным камнем Python как объектно-ориентированного языка программирования является определение связанных классов для управления и обработки данных. Когда мы создаем класс, первым методом, который определяем является метод инициализации __init__ . Если вы примените следующие рекомендации, то тот, кто будет читать ваш код, лучше поймет механику работы всех объектов экземпляра класса. В этой статье я хочу рассказать вам о рекомендациях по определению метода __init__ .

1. Располагайте его в верхней части класса

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

2. Называйте первый параметр self

Прежде всего, вы должны понимать, за что отвечает первый параметр. Он относится к объекту экземпляра, который вызывает метод __init__ . Возможно, вы слышали термин «инстанцирование», однако метод __init__ сам по себе не эквивалентен ему. Как следует из названия, __init__ означает инициализацию, которая отвечает за процесс установки начального состояния созданного экземпляра класса. Есть соглашение, согласно которому первый параметр должен называться self , хотя это и не обязательно. Отмечу, что self – это не ключевое слово в Python, в отличие от многих других языков, где используется this , self или it . Они являются зарезервированными ключевыми словами для ссылки на текущий вызываемый экземпляр.

3. Задайте все атрибуты экземпляра

Python не ограничивает вас в месте определения атрибутов экземпляра класса. Однако это не значит, что вы можете писать их где угодно. Рассмотрим следующий пример:

class Student: def __init__(self, name): self.name = name def verify_registration(self): self.registered = True def get_guardian_name(self): self.guardian = "Someone"

Как показано выше, мы можем создать экземпляр класса Student , инициализировав его имя. Затем мы можем вызвать verify_registration , чтобы получить статус регистрации и get_guardian_name , чтобы получить информацию об опекуне учащегося. Однако так делать нежелательно, поскольку те, кто будут читать ваш код, не будут уверены в том, какие атрибуты есть у экземпляра. Вместо этого лучше поместить все атрибуты в __init__ , чтобы читатели точно знали, какие атрибуты есть у экземпляров. В следующей реализации шаблон гораздо лучше:

class Student: def __init__(self, name): self.name = name self.registered = False self.guardian = None

4. Старайтесь не использовать **kwargs

В Python **kwargs используется для указания различного числа именованных параметров в определениях функций. Таким образом синтаксически правильнее включать **kwargs в __init__ . Конечно, существуют обстоятельства, при которых использовать **kwargs в __init__ допустимо, но в большинстве случаев это не нужно, поскольку так вы маскируете необходимые параметры для инициализации экземпляров.Предполагаю, что одним из основных оправданий использования **kwargs является «чистота» __init__ . Однако я считаю, что явное всегда лучше, чем неявное. Несмотря на то, что перечисление всех параметров в заголовке метода __init__ может показаться громоздким, мы четко даем понять, какие параметры пользователю необходимо указать при создании экземпляров класса.

5. Устанавливайте правильные значения по умолчанию

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

class Student: def __init__(self, name, bus_rider=True): self.name = name self.bus_rider = bus_rider

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

class Student: def __init__(self, name, subjects=["maths", "physics"]): self.name = name self.subjects = subjects

Проблема в том, что, если вы указываете [“maths”, “physics”] как значение по умолчанию, этот список будет создан в определении функции и его же будут использовать все экземпляры. Вот как выглядит эта проблема:

>>> student0 = Student("John") >>> student0.subjects.append("music") >>> student0.subjects ['maths', 'physics', 'music'] >>> student1 = Student("Ashley") >>> student1.subjects ['maths', 'physics', 'music']

Документация

Как и в случае с другими методами, у __init__ тоже должна быть документация. Несмотря на то, что некоторым людям не нравится документировать информацию об архитектуре на уровне класса (то есть помещать информацию об инициализации под заголовком класса), все же рекомендуется размещать документацию прямо под методом __init__ . Для каждого параметра вы указываете его тип – будь то str или int . Исходя из этого, вы даете краткое описание того, что этот параметр из себя представляет и что он делает. Если есть значение по умолчание, расскажите о нем сопроводив доступным пояснением.

Заключение

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

Материал подготовлен в рамках курса «Python Developer. Basic».

Всех желающих приглашаем на онлайн-интенсив «Мобильное приложение для автоматических рассылок с использованием Kivy Framework». За 2 дня интенсива мы создадим мобильное приложение (с использованием Kivy Framework) для планирования автоматических рассылок почтовых сообщений.

  • python
  • __init__
  • метод __init__
  • data science
  • kivy framework
  • создание мобильного приложения
  • Блог компании OTUS
  • Python
  • Программирование

Вероятно, вы неправильно используете метод __init__ в Python

Почему вам следует делать ваши конструкторы простыми

Многие методы __init__ представляют собой сложный лабиринт.

Python — объектно-ориентированный язык. Способ создания нового объекта обычно определяется в специальном методе __init__ , реализованном в классе. Простой класс, хранящий две переменные экземпляра, можно реализовать следующим образом:

class MyClass: def __init__(self, attr1, attr2): self.attr1 = attr1 self.attr2 = attr2 def get_variables(self): return self.attr1, self.attr2 my_object = MyClass("value1", "value2") my_object.get_variables() # -> ("value1", "value2")

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

Однако большинство объектов строятся гораздо сложнее. Часто данные, которые объект должен хранить, недоступны, и их необходимо получить из других входных данных. Зачастую мы хотим иметь возможность создавать один и тот же объект из разных типов аргументов.

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

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

class Configuration: def __init__(self, filepath): self.filepath = filepath self._initialize() def _initialize(self): self._parse_config_file() self._precompute_stuff() def _parse_config_file(self): # распарсить файл self.filepath, и сохранить # данные в нескольких переменных self. . def _precompute_stuff(self): # использовать переменные, определенные в # self._parse_config_file, для вычисления и установки # новых переменных экземпляра . 

Но что в этом плохого? Две вещи:

  1. Очень сложно судить о состоянии объекта при его создании. Какие переменные экземпляра определены и каковы их значения? Чтобы это выяснить, мы должны пройти всю иерархию функций инициализации и принять во внимание любые присвоения self. . В этом фиктивном примере это всё ещё возможно, но я видел примеры, где вызываемый в __init__ код состоит из более чем 1000 строк и включает методы, вызываемые из суперкласса.
  1. Логика создания теперь жёстко запрограммирована. Нет другого способа создать объект Configuration , кроме как указать путь к файлу, поскольку для создания объекта всегда необходимо пройти через метод __init__ . На данный момент мы всегда можем создать Configuration из файла, но кто сказал, что так будет и в будущем? Кроме того, хотя реальному приложению может потребоваться только один способ создания экземпляра, для тестирования может быть удобно создать объект-пустышку, не полагаясь на дополнительный файл.

Эти проблемы обычно проявляются на поздней стадии разработки. Тогда многие разработчики Python пытаются решить проблему, еще больше ухудшая ситуацию. Например:

  • Разрешить входной переменной иметь несколько типов, затем проверить, к какому типу относятся входные данные экземпляра и перейти к другой ветке инициализации в зависимости от результата. В нашем примере мы могли бы изменить входную переменную filepath на config и позволить ей быть строкой или словарём, который мы будем интерпретировать соответственно как путь к файлу или уже проанализированные данные.
  • Добавление аргументов, которые переопределяют друг друга. Например, мы могли бы принять оба аргумента config и filepath и игнорировать filepath , если указан config .
  • Добавление аргументов, которые могут быть логическими значениями или перечислением, для выбора ветвей в логике инициализации. Например, если у нас есть несколько версий одного и того же файла конфигурации, мы можем просто добавить аргумент version в __init__ .
  • Добавление *args или **kwargs в __init__ , потому что тогда сигнатуру __init__ больше не нужно будет менять, но логика реализации может меняться при необходимости.

Почему всё это — плохие решения? По сути, потому что это костыли, решающие одну проблему, но делающие другую ещё хуже. Если у вас возникли проблемы с логикой инициализации и вы используете одну из вышеперечисленных стратегий, подумайте о том, чтобы сделать шаг назад и использовать альтернативный подход.

Чтобы решить проблему, я стараюсь следовать подходу, который заключается в том, чтобы рассматривать почти каждый класс как dataclass или NamedTuple (без обязательного использования этих примитивов напрямую). Это означает, что мы должны думать об объекте не иначе как о наборе связанных данных. Класс определяет имена полей данных и их типы и, при необходимости, реализует методы для работы с этими данными. Метод __init__ не должен делать ничего, кроме присвоения этих данных; его аргументы должны непосредственно соответствовать переменным экземпляра. Многие другие языки имеют встроенную конструкцию для этой концепции: struct .

Почему это предпочтительнее любого другого объекта Python?

  1. Это заставляет вас думать о данных, которые действительно необходимы объекту для функционирования. Это защитит от установки множества бесполезных переменных экземпляра в __init__ «на всякий случай» или, что еще хуже, от установки разных переменных экземпляра в разных ветках.
  2. Это ставит состояние на первый план для читающих код и отделяет его от любой логики манипулирования данными. И сразу позволяет понять, какие атрибуты определены для объекта. Все сгруппировано вместе. В инициализации объекта нет никакой магии.
  3. Это значительно упрощает создание объектов различными способами, например путем определения фабричных методов или конструкторов. Также это облегчит тестирование.

Для иллюстрации давайте посмотрим на альтернативную реализацию нашего класса Configuration :

class Configuration: def __init__(self, attr1, attr2): self.attr1 = attr1 self.attr2 = attr2 @classmethod def from_file(cls, filepath): parsed_data = cls._parse_config_file(filepath) computed_data = cls._precompute_stuff(parsed_data) return cls( attr1=parsed_data, attr2=computed_data, ) @classmethod def _parse_config_file(cls, filepath): # разбираем файл по указанному пути и возвращаем данные . @classmethod def _precompute_stuff(cls, data): # используем данные, полученные из конфигурационного файла, # для расчета новых данных . 

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

Вместо передачи пути к файлу в конструктор теперь у нас есть фабричный метод from_file , реализованный как classmethod . Мы также преобразовали наши методы синтаксического анализа и вычислений в classmethod , которые теперь принимают входные данные и возвращают результаты. Данные, возвращаемые этими методами, передаются конструктору, и возвращается результирующий объект.

Преимущества этого подхода:

  • Легче понять и рассуждать о состоянии. Сразу становится ясно, какие атрибуты экземпляра определяются для объекта после создания экземпляра.
  • Легче тестировать. Наши функции инициализации — это чистые функции, которые можно вызывать изолированно и которые не полагаются на уже существующее состояние объекта.
  • Легче расширять. Мы можем легко реализовать дополнительные фабричные методы для создания объекта Configuration альтернативными способами, например из словаря.
  • Легче быть последовательным. Следовать этому подходу в большинстве ваших классов легче, чем постоянно заново изобретать сложную логику пользовательской инициализации.

Вы также можете рассмотреть возможность полного отделения кода создания от самого класса, например, переместив логику в функцию или класс Factory .

Builders (строители) — это альтернатива фабрикам, когда вам нужен высокий уровень гибкости при создании ваших объектов. Идея состоит в том, чтобы использовать вспомогательный объект « builder » с сохранением состояния, который вы модифицируете, вызывая его методы. Затем, когда желаемое состояние создано, вызов метода типа build создаёт интересующий вас объект. Когда вы обнаружите, что вам нужно много аргументов или много логики в фабричном методе, вы можете рассмотреть шаблон builder . Обратной стороной этого шаблона является то, что его сложнее тестировать.

К сожалению, фабричные методы и строители довольно редки в кодовых базах Python, по крайней мере, в пользовательских API. Многие программисты ожидают, что объекты всегда будут создаваться путем прямого вызова конструктора, и это отражено в API большинства популярных библиотек. Обычно вы хотите предоставить пользователям API, с которым они знакомы. В этом случае вы все равно можете использовать некоторые стратегии, описанные выше, но реализовать собственный метод __new__ , чтобы предоставить знакомый API инициализации.

Ожидания относительно того, как «должен выглядеть» Python, отчасти объясняют, почему методы __init__ имеют тенденцию стремительно усложняться. Но есть и другие причины, связанные с гибкостью Python, из-за которых очень легко сделать неправильный выбор:

  1. Динамическая типизация: переменные могут изменить тип в любое время.
  2. Нет инкапсуляции: все атрибуты общедоступны.
  3. Нет неизменяемости: большинство атрибутов изменяемы.

Это означает, что по умолчанию новым переменным экземпляра может быть присвоено любое значение любого объекта в любое время любым другим объектом. Это здорово, когда нужно найти быстрое решение. В Python никогда не возникает «необходимости» думать о фабриках или конструкторах; вы просто собираете его на лету! Однако это ужасно для создания поддерживаемого кода. Очень сложно отлаживать и анализировать состояние программы, если из кода не ясно, где создаётся или изменяется состояние.

Существует ряд стратегий улучшения, и все они предполагают наложение ограничений на Python.

Во-первых, чтобы решить некоторые проблемы с динамической типизацией, вам следует внедрить статическую проверку типов с помощью mypy (https://www.mypy-lang.org/) и использовать строгие (strict) настройки. Mypy достаточно хорошо понимает состояние объекта, т. е. какие переменные определены в объекте и какие типы им присвоены в методе __init__ . Mypy можно настроить так, чтобы запретить все другие новые присвоения переменных. Это должно защитить вас от некоторых грубых ошибок во время выполнения программы, таких как вызов методов, которые используют несуществующие атрибуты или атрибуты, имеющие значение None . Mypy также не позволяет изменять тип переменной, поэтому вы не сможете быть небрежными с Optional типами, т.е. вы не сможете просто инициализировать переменные как None и позже присвоить им что-то ещё. В конечном счете, статический анализ типов поможет вам выявить проблемы в дизайне: если вы не можете соответствовать требованиям mypy , вам, вероятно, следует переосмыслить свою архитектуру.

Во-вторых, чтобы улучшить инкапсуляцию, сделайте все переменные экземпляра закрытыми, что означает, что доступ к ним можно получить только методами самого объекта. На самом деле это невозможно реализовать в Python, но по соглашению любой атрибут, начинающийся с символа « _ », считается закрытым. Поэтому, если вы обнаружите, что используете метод или переменную, начинающуюся с « _ » вне методов объекта, вам следует пересмотреть свой дизайн. Языковые серверы и IDE будут соблюдать это соглашение и не будут отображать эти методы или переменные в меню автодополнения (если только вы явно не введёте « _ »). Вы можете сделать переменные экземпляра почти полностью приватными, поставив перед ними префикс двойного подчёркивания.

В-третьих, по возможности выбирайте неизменяемость. Статическое состояние гораздо легче понять, чем изменяемое. Обеспечение неизменяемости может быть достигнуто несколькими способами. Если вы используете частные переменные экземпляра и предоставляете их только с помощью метода получения, это способствует неизменяемости. Вы также можете попытаться использовать неизменяемые структуры данных, такие как кортежи, вместо списков. Если вы не можете выбрать между dataclass или NamedTuple , следует отдать предпочтение NamedTuple , поскольку его поля неизменяемы.

Применяя эти дополнительные предложения к нашему предыдущему примеру, мы приходим к следующему:

from __future__ import annotations class Configuration: def __init__(self, attr1: int, attr2: int) -> None: self._attr1 = attr1 self._attr2 = attr2 @property def attr1(self) -> int: return self._attr1 @property def attr2(self) -> int: return self._attr2 @classmethod def from_file(cls, filepath: str) -> Configuration: parsed_data = cls._parse_config_file(filepath) computed_data = cls._precompute_stuff(parsed_data) return cls( attr1=parsed_data, attr2=computed_data, ) @classmethod def _parse_config_file(cls, filepath: str) -> int: # разбираем файл по указанному пути и возвращаем данные . @classmethod def _precompute_stuff(cls, data: int) -> int: # используем данные, полученные из конфигурационного файла, # для расчета новых данных . 

Заключение

Старайтесь, чтобы методы __init__ ваших классов были простыми, и думайте о классах как о структурах. Переместите логику построения объектов в фабричные методы или «builders». Это облегчит чтение вашего кода, его тестирование и расширение в будущем. Кроме того, используйте статический анализ типов, инкапсуляцию и неизменяемость для принятия архитектурных решений и написания более надёжного кода Python.

НЛО прилетело и оставило здесь промокод для читателей нашего блога:

-15% на заказ любого VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.

Конструктор класса – метод __init__

В объектно-ориентированном программировании конструктором класса называют метод, который автоматически вызывается при создании объектов. Его также можно назвать конструктором объектов класса. Имя такого метода обычно регламентируется синтаксисом конкретного языка программирования. Так в Java имя конструктора класса совпадает с именем самого класса. В Python же роль конструктора играет метод __init__ .

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

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

Необходимость конструкторов связана с тем, что нередко объекты должны иметь собственные свойства сразу. Пусть имеется класс Person , объекты которого обязательно должны иметь имя и фамилию. Если класс будет описан подобным образом

class Person: def set_name(self, n, s): self.name = n self.surname = s

то создание объекта возможно без полей. Для установки имени и фамилии метод set_name нужно вызывать отдельно:

>>> from test import Person >>> p1 = Person() >>> p1.set_name("Bill", "Ross") >>> p1.name, p1.surname ('Bill', 'Ross')

В свою очередь, конструктор класса не позволит создать объект без обязательных полей:

class Person: def __init__(self, n, s): self.name = n self.surname = s p1 = Person("Sam", "Baker") print(p1.name, p1.surname)

Здесь при вызове класса в круглых скобках передаются значения, которые будут присвоены параметрам метода __init__ . Первый его параметр – self – ссылка на сам только что созданный объект.

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

>>> p1 = Person() Traceback (most recent call last): File "", line 1, in TypeError: __init__() missing 2 required positional arguments: 'n' and 's'

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

class Rectangle: def __init__(self, w=0.5, h=1): self.width = w self.height = h def square(self): return self.width * self.height rec1 = Rectangle(5, 2) rec2 = Rectangle() rec3 = Rectangle(3) rec4 = Rectangle(h=4) print(rec1.square()) print(rec2.square()) print(rec3.square()) print(rec4.square())
10 0.5 3 2.0

Если класс вызывается без значений в скобках, то для параметров будут использованы их значения по умолчанию. Однако поля width и height будут у всех объектов.

Кроме того, конструктору вовсе не обязательно принимать какие-либо параметры, не считая self .

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

В Python создать несколько методов __init__ в классе можно, однако «рабочим» останется только последний. Он переопределит ранее определенные. Поэтому в Python в классах используется только один конструктор, а изменчивость количества передаваемых аргументов настраивается через назначение значений по-умолчанию.

Практическая работа. Конструктор и деструктор

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

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

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

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

В классах Python функцию деструктора выполняет метод __del__ .

Напишите программу по следующему описанию:

  1. Есть класс Person , конструктор которого принимает три параметра (не учитывая self ) – имя, фамилию и квалификацию специалиста. Квалификация имеет значение заданное по умолчанию, равное единице.
  2. У класса Person есть метод, который возвращает строку, включающую в себя всю информацию о сотруднике.
  3. Класс Person содержит деструктор, который выводит на экран фразу «До свидания, мистер …» (вместо троеточия должны выводиться имя и фамилия объекта).
  4. В основной ветке программы создайте три объекта класса Person . Посмотрите информацию о сотрудниках и увольте самое слабое звено.
  5. В конце программы добавьте функцию input() , чтобы скрипт не завершился сам, пока не будет нажат Enter . Иначе вы сразу увидите как удаляются все объекты при завершении работы программы.

Курс с примерами решений практических работ:
pdf-версия

X Скрыть Наверх

Объектно-ориентированное программирование на Python

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *