711.62K
Категория: ПрограммированиеПрограммирование

Магические методы. Классы. ООП

1.

Магические методы.
Классы. ООП.

2.

Что такое магические методы?
Магические (или Dunder) методы – это специальные методы,
которые начинаются и заканчиваются двойным подчеркиванием.
Такие методы не предназначены для прямого вызова. Их вызов
происходит внутри класса во время выполнения определенного
действия.
Например, самыми простыми магическими методами можно считать
два метода образующих конструктор объекта __new__ и __init__, а
также метод-деструктор __del__. Далее мы поговорим об этих
методах.
В документации Python указано достаточно большое количество
таких методов, однако они не совсем хорошо описаны. В
дальнейшем по ходу занятия попробуем разобраться что это такое и с
чем это “едят”.

3.

К конструктору объекта также относится и инициализация объекта.
Первоначально, давайте создадим простой класс для реализации методов
__new__ и __init__.
Теперь, нужно осознать, что прежде,
чем будет создан новый объект (или экземпляр класса если удобнее) будет
вызван метод __new__. В данном методе определен порядок создания
нашего объекта. Если сейчас мы попробуем создать новый объект класса
Example – он будет успешно создан. Возникает вопрос, почему? Ведь мы
не определяли __new__ для нашего класса.
Ответ кроется в том, что __new__ срабатывает по схеме, которая
определена для него по умолчанию, поэтому даже в нашем примере при
создании объекта мы можем его создать. Однако остается еще один
вопрос, зачем нам в принципе нужен метод, который уже определен по
умолчанию и в целом и раньше мы могли его использовать.
Ответ кроется в том, что иногда возникают ситуации, при которых еще до
начала создания объекта нам нужно обеспечить специальную логику его
создания. На этом, например, основан паттерн Singleton.

4.

Пара слов о паттерне Singleton
Синглтон (одиночка) – суть паттерна Singleton в
предоставлении механизма создания одного и только одного
экземпляра объекта, при этом предоставляя к нему глобальную точку
доступа.
Обычно Singleton обычно используется в таких случаях как ведение
журнала или операции с базой данных.
Например, мы можем захотеть использовать один объект базы
данных для выполнения операций с БД для опеспечения
согласованности данных или один объект класса ведения журнала
для нескольких служб, чтобы последовательно выгружать сообщения
журнала в определенный файл журнала.
С примером реализации Singleton можете ознакомиться по ссылке:
https://webdevblog.ru/realizaciya-shablona-singleton-v-python/

5.

Посмотрим на примере как работает переопределенный метод __new__

6.

__new__
• В итоге еще на этапе создания объекта мы
добавили новый атрибут в его рабочую область.
Кроме добавления новых атрибутов мы также
можем определять процесс создания новых
объектов на базе нашего класса.
• Что касается классов, в которых __new__ не
определен явно – всё просто. Данный метод
будет срабатывать как унаследованный из
класса object, т.к. все классы в Python
наследуются от него.

7.

Конструктор объекта (инициализатор). __init__
Магический метод __init__ - является инициализатором класса. Ему
передается всё, с чем был вызван первоначальный конструктор __new__.
В предыдущем примере мы переопределяли наш метод __new__, для
которого почти всегда мы будем в качестве параметров указывать *args,
**kwargs. Это следует делать для того, чтобы в последующем в наш __init__
были переданы все позиционные и ключевые аргументы, которые
изначально передавались в __new__ на этапе создания объекта.

8.

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

9.

Конструктор объекта (инициализатор). __init__
Мы убедились, что прежде, чем аргументы
попадают в __init__ они изначально попадают в
*args **kwargs метода __new__.
Т.е. как было сказано выше изначально создаётся
объект, а уже после он инициализируется.
В отличие от __new__ - __init__ всегда
используется при определении классов.

10.

Деструктор объекта . __del__
Если __new__ и __init__ образуют конструктор объекта, то __del__ это его деструктор. Простыми словами это механизм удаления
объекта.
__del__ Определяет поведение объекта в то время, когда объект
попадает в сборщик мусора. Это может быть даволно удобно для
объектов, которые могут требовать дополнительных чисток во время
удаления, такие как файловые объекты. Использовать и изменять
механизм работы деструктора не желательно и крайне не
рекомендуется, за исключением “крайних случаев”.
Python Garbage Collection (Уборка мусора).
Ранее думаю Вы не раз слышали о том, что Python удаляет объекты,
на которые больше нет ссылок в программе, чтобы освободить
пространство памяти. Этот процесс, в котором Python освобождает
блоки памяти, которые больше не используются, называется Garbage
Collection. Python GC работает во время выполнения программы и
запускается, если счетчик ссылок увеличивается, если объекту
присваивается новое имя или он помещается в контейнер, такой как
кортеж или словарь.

11.

Практика
1. Создайте класс FileObject.
2. Для данного класса определите магический метод __init__,
параметрам которого будет filename.
3. Создайте любой .txt файл в директории Вашего проекта.
Создайте в нем по 5 слов на каждой строке файла.
После того, как работа с файлом окончена его важно не
забыть закрыть, это делается с помощью метода close().
Делается это для того, чтобы освободить место в памяти.
4. Объявите магический метод __del__ таким образом, чтобы
в момент вызова данного метода файл автоматически
закрывался, а после удалялся.
5. Реализуйте метод read_file, который выведет в терминал
содержимое файла, переданного в filename

12.

Решение

13.

Переопределение операторов на произвольных классах
Отличным преимуществом использования магических методов
в Python является то, что они предоставляют простой способ
заставить объекты вести себя по подобию встроенных типов.
Это означает, что Вы можете избежать нелогичного и
нестандартного поведения базовых операторов.
Простыми словами говоря, используя некоторые магические
методы мы можем определить, как будут вести себя классы,
объекты которых сравниваются с другими объектами с
помощью встроенных операторы сравнения ( >, <, ==, !=).
Это одна из сильнейших сторон магических методов.
Подавляющее большинство их них позволяют определить, что
будут делать стандартные операторы, так что мы можем
использовать операторы на своих классах так, как будто они
встроенные типы.

14.

Некоторый перечень таких методов
• __eq__(self, other)
Определяет поведение оператора равенства, ==.
• __ne__(self, other)
Определяет поведение оператора неравенства, !=.
• __lt__(self, other)
Определяет поведение оператора меньше, <.
• __gt__(self, other)
Определяет поведение оператора больше, >.
• __le__(self, other)
Определяет поведение оператора меньше или равно, <=.
• __ge__(self, other)
Определяет поведение оператора больше или равно, >=

15.

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

16.

Пример

17.

Тестируем

18.

Примечание
Теперь мы можем создать два объекта Word и сравнить их по длине.
Тоже мы можем делать и с обычными строками.
Заметьте, что мы не определяли __eq__ и __ne__ (== и !=), так как
это приведёт к странному поведению .
(например, Word('foo') == Word('bar') будет расцениваться как
истина). В этом нет смысла при тестировании на
эквивалентность, основанную на длине, поэтому мы оставляем
стандартную проверку на эквивалентность от str.
Сейчас, кажется, удачное время упомянуть, что вы не должны
определять каждый из магических методов сравнения, чтобы
полностью охватить все сравнения. Стандартная библиотека любезно
предоставляет нам класс-декторатор в модуле functools, который и
определит все сравнивающие методы, от вас достаточно определить
только __eq__ и ещё один (__gt__, __lt__ и т.п.) Эта возможность
доступна начиная с 2.7 версии Питона, но если это вас устраивает, вы
сэкономите кучу времени и усилий. Для того, чтобы задействовать
её, поместите @total_ordering над вашим определением класса.

19.

Пример с @total_ordering из functools

20.

Числовые магические методы
Точно так же, как вы можете определить, каким образом
ваши объекты будут сравниваться операторами
сравнения, вы можете определить их поведение для
числовых операторов. В целом разделяют 5 категорий:
• унарные операторы
• обычные арифметические операторы
• отражённые арифметические операторы
• составные присваивания
• преобразования типов.

21.

Магические методы.
Унарные операции и
функции

22.

Унарные операторы и функции
Унарные операторы и функции имеют только один операнд — отрицание,
абсолютное значение, и так далее. Унарный – означает, что он применяется
только к одному операнду.
• __pos__(self)
Определяет поведение для унарного плюса (+some_object)
• __neg__(self)
Определяет поведение для отрицания(-some_object)
• __abs__(self)
Определяет поведение для встроенной функции abs().
• __invert__(self)
Определяет поведение для инвертирования оператором ~.
• __round__(self, n)
Определяет поведение для встроенной функции round(). n это число знаков
после запятой, до которого округлить.
• __floor__(self)
Определяет поведение для math.floor(), то есть, округления до ближайшего
меньшего целого.
• __ceil__(self)
Определяет поведение для math.ceil(), то есть, округления до ближайшего
большего целого.
• __trunc__(self)
Определяет поведение для math.trunc(), то есть, обрезания до целого.

23.

Обычные математические операторы
• __add__(self, other)
Сложение.
• __sub__(self, other)
Вычитание.
• __mul__(self, other)
Умножение.
• __floordiv__(self, other)
Целочисленное деление, оператор //.
__div__(self, other)
Деление, оператор /
• __truediv__(self, other)
Правильное деление. Заметьте, что это работает только когда
используется from __future__ import division.
__mod__(self, other)
Остаток от деления, оператор %.

24.

Обычные математические операторы
• __divmod__(self, other)
Определяет поведение для встроенной функции divmod().
__pow__
Возведение в степень, оператор **.
__lshift__(self, other)
Двоичный сдвиг влево, оператор <<.
__rshift__(self, other)
Двоичный сдвиг вправо, оператор >>.
__and__(self, other)
Двоичное И, оператор &.
__or__(self, other)
Двоичное ИЛИ, оператор |.
__xor__(self, other)
Двоичный xor, оператор ^.

25.

Магические методы.
Отраженные арифметические
операторы

26.

Отраженные арифметические операторы
В данной механике нет ничего сверхъестественного и
сложного. Единственное, чем отличается эквивалентное
отраженное выражение это порядок слагаемых
other (другой объект) + объект нашего класса.
Таким образом, все эти магические методы делают то же
самое, что и их обычные версии, за исключением выполнения
операции с other в качестве первого операнда и объекта класса
(пользовательского) в качестве второго.

27.

Отраженные арифметические операторы
• __radd__(self, other)
Отражённое сложение.
• __rsub__(self, other)
Отражённое вычитание.
• __rmul__(self, other)
Отражённое умножение.
• __rfloordiv__(self, other)
Отражённое целочисленное деление, оператор //.
• __rdiv__(self, other)
Отражённое деление, оператор /.
• __rtruediv__(self, other)
Отражённое правильное деление. Заметьте, что работает только когда
используется from __future__ import division.

28.

Отраженные арифметические операторы
• __rmod__(self, other)
Отражённый остаток от деления, оператор %.
• __rdivmod__(self, other)
Определяет поведение для встроенной функции divmod(), когда
вызывается divmod(other, self).
• __rpow__
Отражённое возведение в степерь, оператор **.
• __rlshift__(self, other)
Отражённый двоичный сдвиг влево, оператор <<.
• __rrshift__(self, other)
Отражённый двоичный сдвиг вправо, оператор >>.
• __rand__(self, other)
Отражённое двоичное И, оператор &.
• __ror__(self, other)
Отражённое двоичное ИЛИ, оператор |.
• __rxor__(self, other)
Отражённый двоичный xor, оператор ^.

29.

Магические методы.
Составное присваивание

30.

Составное присваивание
В Python широко представлены и магические методы для составного
присваивания. Вы скорее всего уже знакомы с составным присваиванием,
это комбинация “обычного” оператора и присваивания. Пример:
Каждый из таких методов должен возвращать значение, которое будет
присвоено переменной слева (например,
для a += b метод __iadd__ должен вернуть a + b, что будет присвоено a.

31.

Составное присваивание
• __iadd__(self, other)
Сложение с присваиванием.
• __isub__(self, other)
Вычитание с присваиванием.
• __imul__(self, other)
Умножение с присваиванием.
• __ifloordiv__(self, other)
Целочисленное деление с присваиванием, оператор //=.
• __idiv__(self, other)
Деление с присваиванием, оператор /=.
• __itruediv__(self, other)
Правильное деление с присваиванием. Заметьте, что работает только если
используется from __future__ import division.

32.

Составное присваивание
• __imod_(self, other)
Остаток от деления с присваиванием, оператор %=.
• __ipow__
Возведение в степерь с присваиванием, оператор **=.
• __ilshift__(self, other)
Двоичный сдвиг влево с присваиванием, оператор <<=.
• __irshift__(self, other)
Двоичный сдвиг вправо с присваиванием, оператор >>=.
• __iand__(self, other)
Двоичное И с присваиванием, оператор &=.
• __ior__(self, other)
Двоичное ИЛИ с присваиванием, оператор |=.
• __ixor__(self, other)
Двоичный xor с присваиванием, оператор ^=

33.

Магические методы
преобразования типов.

34.

Магические методы преобразования типов
В Python множество магических методов, предназначенных для определения поведения для встроенных функций
преобразования типов (int(), float(), bool() и т.д.)
• __int__(self)
Преобразование типа в int.
• __long__(self)
Преобразование типа в long.
• __float__(self)
Преобразование типа в float.
• __complex__(self)
Преобразование типа в комплексное число.
• __oct__(self)
Преобразование типа в восьмеричное число.
• __hex__(self)
Преобразование типа в шестнадцатиричное число.
• __index__(self)
Преобразование типа к int, когда объект используется в срезах (выражения вида [start:stop:step]). Если вы
определяете свой числовый тип, который может использоваться как индекс списка, вы должны
определить __index__.
• __trunc__(self)
Вызывается при math.trunc(self). Должен вернуть своё значение, обрезанное до целочисленного типа (обычно
long).
• __coerce__(self, other)
Метод для реализации арифметики с операндами разных типов. __coerce__ должен вернуть None если
преобразование типов невозможно. Если преобразование возможно, он должен вернуть пару (кортеж из 2-х
элементов) из self и other, преобразованные к одному типу.

35.

Магические методы.
Представление своих
классов.

36.

Магические методы представления своих классов
Часто бывает полезно представление класса в виде строки. В Python
существует несколько методов, которые вы можете определить для
настройки поведения встроенных функций при представлении вашего
класса.
• __str__(self)
Определяет поведение функции str(), вызванной для экземпляра вашего
класса. Если __str__ не определен в классе, то его значение будет взято из
__repr__.
• __repr__(self)
Определяет поведение функции repr(), вызыванной для экземпляра вашего
класса. Главное отличие от str() в целевой аудитории. repr() больше
предназначен для машинно-ориентированного вывода (более того, это часто
должен быть валидный код на Питоне), а str() предназначен для чтения
людьми. Выводом метода по умолчанию будет объект памяти, в которой
хранится этот объект.

37.

Магические методы представления своих классов
• __unicode__(self)
Определяет поведение функции unicode(), вызыванной для экземпляра
вашего класса. unicode() похож на str(), но возвращает строку в юникоде.
Будте осторожны: если клиент вызывает str() на экземпляре вашего класса, а
вы определили только __unicode__(), то это не будет работать. Постарайтесь
всегда определять __str__() для случая, когда кто-то не имеет такой роскоши
как юникод.
• __format__(self, formatstr)
Определяет поведение, когда экземпляр вашего класса используется в
форматировании строк нового стиля. Например, "Hello,
{0:abc}!".format(a) приведёт к вызову a.__format__("abc"). Это может быть
полезно для определения ваших собственных числовых или строковых
типов, которым вы можете захотеть предоставить какие-нибудь
специальные опции форматирования.
• __hash__(self)
Определяет поведение функции hash(), вызыванной для экземпляра вашего
класса. Метод должен возвращать целочисленное значение, которое будет
использоваться для быстрого сравнения ключей в словарях. Заметьте, что в
таком случае обычно нужно определять и __eq__ тоже. Руководствуйтесь
следующим правилом: a == b подразумевает hash(a) == hash(b).

38.

Магические методы представления своих классов
• __nonzero__(self)
Определяет поведение функции bool(), вызванной для экземпляра вашего
класса. Должна вернуть True или False, в зависимости от того, когда вы
считаете экземпляр соответствующим True или False.
• __dir__(self)
Определяет поведение функции dir(), вызванной на экземпляре вашего
класса. Этот метод должен возвращать пользователю список атрибутов.
Обычно, определение __dir__ не требуется.
• __sizeof__(self)
Определяет поведение функции sys.getsizeof(), вызыванной на экземпляре
вашего класса. Метод должен вернуть размер вашего объекта в байтах. Он
главным образом полезен для классов, определённых в расширениях на C,
но всё-равно полезно о нём знать.
English     Русский Правила