0:03
Давайте продолжим говорить о классах и экземплярах.
И сейчас мы посмотрим на то, что такое атрибуты класса.
Иногда вам нужно создать переменную, которая будет работать
в контексте класса, но которая не связана с каждым
конкретным экземпляром. То есть, эта
переменная относится непосредственно к самому классу, а не к экземпляру.
Такая переменная называется атрибутом класса и на слайде
вы можете видеть ее.
Вот этот атрибут класса, атрибут, который мы назвали сount.
В данном случае мы в методе _init_ для каждого экземпляра
увеличиваем количество планет, которое было создано,
обращаясь к атрибуту класса через имя класса.
На практике это может быть иногда полезно.
Давайте посмотрим, как это сделать.
Мы объявляем
две переменных Earth и Mars. Это два экземпляра класса Planet.
И после этого, когда мы обращаемся к атрибуту сount,
несмотря на то, что атрибут сount не является атрибутом экземпляра,
мы получаем его значение.
Мы также можем обратиться к атрибуту сount через объект
экземпляр класса и также получим его значение.
В этот момент Python видит, что внутри экземпляра класса такого
атрибута нет, и он проверяет сам класс на наличие атрибута класса
и находит его.
На самом деле, нужно быть осторожным, когда используешь атрибуты класса.
Часто может возникнуть путаница, когда мы хотим обращаться
к атрибуту как к атрибуту, привязанному к конкретному экземпляру,
а на самом деле это атрибут класса. Но за этим нужно следить.
Это достаточно неявно, но это не так сложно.
Деструктор экземпляра класса. Что это такое?
Когда счетчик ссылок на экземпляр класса достигает 0, помните,
мы уже с вами говорили про сборщик мусора в Python и то,
что он использует счетчик ссылок, вызывается метод __del__ экземпляра.
Это также магический метод, который Python нам предоставляет
возможность переопределить.
В данном случае мы переопределяем этот метод для экземпляра и
печатаем на экран слово "goodbye".
Если мы создадим экземпляр класса, а затем воспользуемся
конструкцией _del_ ,чтобы удалить, в этот момент вызовется как раз
этот магический метод _del_ и напечатается "goodbye".
Однако, на практике лучше магический метод _del_ не переопределять,
потому что нет гарантии, что, по завершению работы интерпретатора
Python, он будет вызван.
Лучше явно определить метод,
который будет выполнять какие-то действия, которые вам нужны.
Какие вообще это могут быть действия? Например, вы можете закрыть файл
в методе _del_ , либо разорвать сетевое соединение.
Так вот, эти действия лучше описывать явно в методах экземпляра,
о которых мы будем говорить с вами в следующем видео.
Словарь экземпляра и класса.
Давайте посмотрим на пример и увидим, что это знакомый нам класс Planet,
у которого переопределен метод _init_,
у которого ставятся атрибуты name и population (население планеты).
А также есть атрибут класса.
Если мы посмотрим на
свойство __dict__ у экземпляра класса Planet, то мы увидим, что это словарь,
у которого есть как раз атрибуты нашего класса name и population.
Когда мы обращаемся к каким-то атрибутам, Python, в первую очередь,
смотрит в эту структуру данных, в этот словарь и ищет там эти атрибуты.
Если мы добавляем атрибут экземпляру нашего класса, в данном случае
на слайде мы добавляем массу планете Земля, то в словаре
экземпляра класса появляется этот атрибут.
Соответственно, в следующий раз когда мы пытаемся получить к нему доступ,
Питон находит этот атрибут в этом словаре и выдает нам его значение.
Если мы посмотрим на атрибут __dict__ у, непосредственно, класса,
а не у экземпляра, то мы увидим, что он также присутствует.
И это объект mapping proxy, который похож на словарь,
у которого также есть множество всевозможных ключей, в том числе,
это ключ __doc__. Это тот docstring, который мы прописали нашему классу.
Также, обратите внимание, что именно в словаре самого класса
присутствует атрибут класса сount.
Если мы обратимся к свойству __doc__, например, то
это свойство будет найдено в этом словаре и Python
вернет нам его значение.
Как я уже говорил, мы можем обращаться к свойствам, которые есть
в словаре класса и от экземпляра конкретного.
То есть, если мы обратимся к свойству __doc__ конкретного экземпляра
Planet, который является объектом класса Planet,
мы также увидим на экране нашу строку.
Если мы посмотрим, какие еще есть методы у экземпляра класса, мы увидим,
что их, на самом деле, очень много.
Например, есть __hash__(). Как я говорил, экземпляры класса хешируемые
и могут быть использованы в ключах словаря.
А также есть очень много других магических методов,
про которые мы с вами еще поговорим.
Также в любой момент мы от экземпляра можем получить класс,
к которому он принадлежит, используя свойство __class__.
И видим, что planet.__class__ нам возвращает __main__.planet,
наш класс.
И последнее, на что мы посмотрим, это на конструктор экземпляра класса.
Давайте посмотрим на пример.
Конструктор экземпляра класса позволяет нам переопределить
какие-то действия, которые
происходят с экземпляром до его инициализации.
На самом деле, на практике это встречается нечасто, но знать
об этом нужно.
На следующих неделях мы вам покажем пример использования
магического метода __new__, который как раз является конструктором
экземпляра класса, в рамках использования метаклассов.
А сейчас давайте просто посмотрим на то, в каком порядке вызывается
магический метод __new__, который мы сейчас переопределим,
и магический метод __init__ , который инициализирует класс.
Здесь на примере мы переопределили магический метод __new__,
который принимает первым аргументом класс, это как раз в нашем
случае класс Planet.
И здесь мы сделали то же самое, что делает Python,
ничего не изменив, за исключением того, что добавили print,
что был вызван метод __new__.
Давайте посмотрим, что значит вот эта строка.
Вот эта строка - это как создание нового экземпляра класса, который еще
не инициализирован.
Метод super возвращает родителя нашего класса,
в данном случае это Object.
Это класс, от которого наследуются все пользовательские классы в Python 3.
Мы вызываем метод __new__ класса Object. И нам возвращается экземпляр.
Этот экземпляр мы просто возвращаем.
Когда мы создаем экземпляр класса Planet , мы видим, что
сначала вызывается метод __new__, а затем вызываем метод __init__.
То есть, сначала создается экземпляр, а затем он инициализируется.
То есть, если переложить это на код, происходит примерно следующее:
сначала вызывается метод __new__,
который получает на вход класс,
в данном случае Planet.
А вторым аргументом то, что мы передаем при создании экземпляра,
наши аргументы, в данном случае строка Earth.
И дальше, если конструктор __new__ вернул нам правильный класс,
если он, соответствуя типу, с помощью функции isinstance проверяет Python,
то вызывается метод __init__.
То есть, происходит инициализация нашего объекта,
используя те аргументы, которые мы передавали
для того, чтобы создать экземпляр.
В этом видео мы посмотрели на то, что такое атрибут класса,
посмотрели на деструктор и конструктор экземпляра.
А также поговорили о поиске атрибутов
в словаре экземпляра и класса.