В рамках данного урока поговорим об объектной модели Python: в чем ее особенность и как ее можно использовать в своей работе.
Что такое объектная модель Python?
Самым лаконичным ответом на вопрос: “что такое объектная модель Python“, будет цитата из википедии, которую также приводит в своей книге Лучано Рамальо: the properties of objects in general in a specific computer programming language, technology, notation or methodology that uses them. Что можно перевести как “общие свойства объектов в конкретном языке программирования, технологии, нотации или методологии”.
Красота объектной модели в Python состоит в том, что если на уровне класса реализовать определенный функционал, то с объектами этого класса можно будет работать используя “питонический стиль” – стиль написания программного кода на Python, который отличается большей выразительностью, читаемостью и понятностью. Такой стиль называют идиоматический – использующий наиболее естественные для данного языка решения.
Для того, чтобы понять о чем идет речь, рассмотрим пример: списковое включение (list comprehensions).
Напишем код, который создает список из квадратов первых пять целых чисел:
sq = [] for i in range(1, 6): sq.append(i**2)
Теперь напишем этот же код с использованием спискового включения:
sq = [i**2 for i in range(1, 6)]
Не правда ли, второй вариант более лаконичный, красивый и понятный? (Хотя мы не отрицаем, что возможно есть люди, которым больше нравится первый вариант).
Всю мощь объектной модели Python можно осознать через изучение назначения и возможностей специальных методов. Это методы, которые вызываются интерпретатором для выполнения различных операций над объектами. В своем коде, программисты, как правило, напрямую не вызывают эти методы. Объектная модель – это своеобразный API, который вы можете реализовать, чтобы ваши объекты можно было использовать максимально естественно для заданной экосистемы (в нашем случае – Python).
Синонимом “объектной модели” является “модель данных”. В Python документации есть целый раздел, посвященный этой теме https://docs.python.org/3/reference/datamodel.html.
Специальные методы
Рассмотрим некоторые из специальных методов (самые интересные, как нам показалось). Более подробную информацию о них вы можете найти в соответствующей документации по Python.
__repr__()
Строковое представление объекта, позволяющее восстановить объект. Например, если вы пишете игру и у вас есть объект “Персонаж”, которому при его создании присваивается имя и пол, то метод __repr__() может вернуть следующую строку “Person(“John”, “man”)”. Эта информация позволит вам заново построить объект.
__str__()
Строковое представление объекта, используемое в функциях format() и print(). В отличии от __repr__(), в ней, при реализации, мы стараемся создать наиболее информативное представление объекта.
__bytes__()
Метод возвращает объект типа bytes – представление текущего объекта в виде набора байт.
__bool__()
Возвращает True или False.
__call__()
Позволяет использовать объект как функцию, т.е. его можно вызвать.
__len__()
Чаще всего реализуется в коллекциях и сходными с ними по логике работы типами, которые позволяют хранить наборы данных. Для списка (list) __len__() возвращает количество элементов в списке, для строки – количество символов в строке. Вызывается функцией len(), встроенной в язык Python.
Функции сравнения
Функция | Операция | Функция | Операция | Функция | Операция |
__lt__() | < | __le__() | <= | __eq__() | == |
__ne__() | != | __gt__() | > | __ge__() | >= |
Эти функции используются, если объекты можно сравнивать между собой, как числа. Синтаксис работы с ними выглядит следующим образом: x.__lt__(y), что соответствует записи x < y.
Функции, эмулирующие числовые типы
Арифметические операции
Функция | Операция | Описание / пример |
__add__() | + | Сложение |
__sub__() | – | Вычитание |
__mul__() | * | Умножение |
__truediv__() | / | Деление |
__floordiv__() | // | Целая часть от деления |
__mod__() | % | Остаток от деления |
__pow__() | ** | Возведение в степень |
Помимо указанных в таблице, в объектной модели Python, есть методы, позволяющие выполнять инверсные арифметические операции (случай, когда объекты, над которыми производится действие меняются местами), имена таких методов совпадают с перечисленными выше с добавлением символа r перед именем (примеры: __radd__(), __rsub__()). Методы для арифметических операции присваивания, таких как *=, += и т.п., имеют имена сходные с табличными с добавлением символа i перед именем (примеры: __iadd__(), __isub__()).
Унарные операции
Функция | Операция | Описание / пример |
__neg__() | – | Отрицательное значение |
__pos__() | + | Положительное значение |
__abs__() | abs() | Модуль числа |
Битовые операции
Функция | Операция | Описание / пример |
__lshift__() | << | Поразрядный сдвиг влево |
__rshift__() | >> | Поразрядный сдвиг вправо |
__and__() | & | Логическое И |
__or__() | | | Логическое ИЛИ |
__xor__() | ^ | Исключающее ИЛИ |
__invert__() | ~ | Инверсия |
Для битовых операций, также как для арифметических, предполагается возможность реализовывать инверсные операции и битовые операции присваивания.
Примеры
Для иллюстрации использования возможностей объектной модели Python приведем канонический пример с классом, в рамках которого реализуется поддержка операций над векторами.
class Vector(): def __init__(self, x, y): self.x = x self.y = y def __add__(self, v): return Vector(self.x + v.x, self.y + v.y) def __sub__(self, v): return Vector(self.x - v.x, self.y - v.y) def __repr__(self): return "Vector({}, {})".format(self.x, self.y) def __str__(self): return "({}, {})".format(self.x, self.y) def __abs__(self): return (self.x**2 + self.y**2)**0.5 if __name__ == "__main__": v1 = Vector(3, 4) print("vector v1 = {}".format(v1)) v2 = Vector(5, 7) print("vector v2 = {}".format(v2)) print("v1 + v2 = {}".format(v1 + v2)) print("v2 - v1 = {}".format(v2 - v1)) print("|v1| = {}".format(abs(v1)))
Запустив эту программу, вы увидите следующее:
>python object_model.py vector v1 = (3, 4) vector v2 = (5, 7) v1 + v2 = (8, 11) v2 - v1 = (2, 3) |v1| = 5.0
Операции сложения и вычитании векторов реализованы в методах __add__() и __sub__(). Модуль вектора – в __abs__(). Функция print(), если ей передать объект типа Vector, в первую очередь вызовет метод __str__(), если этого метода не будет, то вызовется __repr__().
Для эксперимента можете удалить метод __str__() и получите следующий результат:
>python object_model.py vector v1 = Vector(3, 4) vector v2 = Vector(5, 7) v1 + v2 = Vector(8, 11) v2 - v1 = Vector(2, 3) |v1| = 5.0
Вот ещё один пример, который демонстрирует работу с операторами сравнения:
class UserLevel(): us_dict = {"Admin":4, "Operator":3, "Dispatcher":2, "Guest":1} def __init__(self, user_level): if user_level in self.us_dict: self.u_level = user_level else: print("Wrong user type!") self.u_level = "Guest" def diff(self, level1, level2): return self.us_dict[level1] - self.us_dict[level2] def __lt__(self, user): return True if self.diff(self.u_level, user.u_level) < 0 else False def __le__(self, user): return True if self.diff(self.u_level, user.u_level) <= 0 else False def __eq__(self, user): return True if self.diff(self.u_level, user.u_level) == 0 else False def __ge__(self, user): return True if self.diff(self.u_level, user.u_level) >= 0 else False def __gt__(self, user): return True if self.diff(self.u_level, user.u_level) > 0 else False def __repr__(self): return "UserLevel({})".format(self.u_level) def __str__(self): return self.u_level if __name__ == "__main__": need_level = UserLevel("Operator") print("need level: {}".format(need_level)) user_level = UserLevel("Dispatcher") print("user: {}".format(user_level)) if user_level >= need_level: print("Access enabled!") else: print("Access denied!")
В этом примере реализован класс UserLevel, который используется для хранения уровня доступа. Объекты этого типа можно сравнивать между собой, используя классические операции сравнения, поддержка этого функционала реализуется через методы, перечисленные в блоке Битовые операции раздела Специальные методы этой статьи.
Что еще почитать на эту тему?
На эту тему интересно написано у Лучано Рамальо в книге “Python. К вершинам мастерства”. Дополнительно рекомендуется обратить внимание на “Python in a Nutshell” Алекса Мартелли и “Python Essential Reference” Девида Бизли.
P.S.
Если вам интересна тема анализа данных, то мы рекомендуем ознакомиться с библиотекой Pandas. На нашем сайте вы можете найти вводные уроки по этой теме. Все уроки по библиотеке Pandas собраны в книге “Pandas. Работа с данными”.
Здравствуйте, спасибо большое за прекрасные статьи. В этом уроки для начинающих закончились?
Если закончились то дальше изучать по определенным направлениям в Python?
Добрый день!
Нет, не закончились))) Просто пока времени нет активно их писать! Но обязательно буду! Спасибо вам за отзыв! Для дальнейшего развития советую “Python К вершинам мастерства” Лучано Рамальо.
Здравствуйте. У вас в примере кода векторов, который идёт после слов “Запустив эту программу, вы увидите следующее:”, на 5 строке идёт не верный результат.
Должно же быть: v2 – v1 = (2, -3)
Вроде мелочь, но в глаза бросается)
Почему, вроде все корректно, у нас есть вектора
vector v1 = (3, 4)
vector v2 = (5, 7)
теперь мы их вычитаем:
v2-v1=(5-3, 7-4)=(2,3)
Спасибо! Очень интересный урок. Обязательно буду изучать другие уроки. За примеры отдельное спасибо.
Спасибо, Вам за отзыв!