Методы класса – работают с классом, а не экземпляром класса. Нулевым аргументом метода будет cls – текущий класс. Методы класса задаются декоратором @classmethod
Статические методы – не обращаются к полям класса и экземпляра. Задаются декоратором @staticmethod, не принимают никакого нулевого аргумента
Можем задать __eq__ и один из операторов <,<=,>, >= и поставив перед классом декоратор @functools.total_ordering, он определит всё остальное
__len__
__getitem__ и __setitem__, __delitem__ – работа с [] в словарях
__hash__
__call__
__bool__
__iter__, __next__
__dict__
5 of 12
Приватные/защищенные атрибуты
В питоне принято начинать название атрибута с _ – уровень protected на согласовании на уровне разработчика (его не надо использовать вне класса, только внутри). Но можем спокойно обратиться извне класса
Приватные атрибуты – начинаются с __. К ним уже нельзя обратиться вне класса (AttributeError). Сокрытие обработки защищенных атрибутов. При этом предоставляем публичный метод (часто обусловлено требованием консистентности данных!)
Приватные методы также должны начинаться с __
Но функция dir может спалить эти защищенные атрибуты… 🤷 Таким образом в питоне нельзя сделать честное приватное поле. Но вроде как есть модуль accessify, в котором есть декораторы protected и private
6 of 12
property (свойство)
Позволяет использовать методы в качестве свойств объектов — порождает дескриптор, позволяющий создавать «вычисляемые» свойства (тип property).
Можно использовать @property (по умолчанию, только для чтения)
Объект свойства также предоставляет методы getter, setter, deleter, которые можно использовать в качестве декораторов для указания функций реализующих получение, установку и удаление свойства соответственно.
Как же можно внутри класса обратиться к атрибутам класса??
Через self. Так как этого метода нет в экземпляре, он будет искаться в классе
Через имя класса (имена доступны)
Декоратор property и self
создать classmethod – обращение через cls
staticmethod – через имя класса
8 of 12
Новый взгляд и обобщение вышесказанного
Класс — это пространство имён
Класс — это изготовитель (чтобы не сказать «конструктор») объекта — экземпляра класса, т. е. такой callable. У класса (объекта) есть поля(атрибуты). Callable-поля называются методы, не-callable – поля.
Все поля класса видны в объекте
До тех пор, пока их не загородят уникальные поля объекта с теми же именами (аналог: глобальные и локальные переменные)
При этом функции класса превращаются в методы объекта
Метод — это такая функция внутри пространства имён объекта, при вызове которой в начало кортежа параметров ставится сам объект
Специальный метод __init__() нужен для создания полей объекта и вообще логики его создания. Это не конструктор!
9 of 12
Полиморфизм в Python
Пример: помещаем объекты разных классов в список. Причем классы имеют методы с одним и тем же названием, но разной имплементацией
Если вызывать этот метод от объектов, итерируясь по списку, то всё отработает корректно!
С виду мы обращаемся к одному и тому же методу, но питон будет сам понимать, у объекта какого класса он будет вызывать метод! Это полиморфное поведение
Аналогично с магическими методами
10 of 12
Наследование
Наследование классов в Python происходит путем перечисления классов-родителей в скобках после объявления имени класса
isinstance(ex, Class) – является ли ex экземпляром Class или его потомков
type(ex) == Class – точное сравнение: является ли ex экземпляром Class
issubclass(Class, BaseClass) – является ли класс потомком заданного
В классах-наследниках можно определять новые методы или переопределять методы родительского класса.
Все классы в Python унаследованы от класса object
Иногда бывает проблематично унаследоваться от объектов базовых типов. Но на помощь (неожиданно) может прийти модуль collections
11 of 12
Наследование
Видимость для наследника:
Поля объекта
Поля класса
Поля базового класса
Идиома для вызова конструктора текущего класса – type(self)
super() возвращает пространство имён, содержащее поля базового класса
при создании super()-объекта не создаётся экземпляр базового класса (например, не вызывается __new__() и т. п.)
Проблема ромбовидного наследования:
Обход в глубину (слева) добирается до A.v раньше, чем до C.v
Обход в ширину (справа) добирается до A.v раньше, чем до B.v
Множественное наследование и mro (__mro__()) – линеаризация