Python. Урок 20. Объектная модель в Python

В рамках данного урока поговорим об объектной модели 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. Работа с данными”.
Книга: Pandas. Работа с данными

<<<Python. Урок 19. Декораторы функций в Python

Python. Урок 20. Объектная модель в Python: 6 комментариев

  1. Doda

    Здравствуйте, спасибо большое за прекрасные статьи. В этом уроки для начинающих закончились?
    Если закончились то дальше изучать по определенным направлениям в Python?

    1. writer

      Добрый день!
      Нет, не закончились))) Просто пока времени нет активно их писать! Но обязательно буду! Спасибо вам за отзыв! Для дальнейшего развития советую “Python К вершинам мастерства” Лучано Рамальо.

  2. Евгениц

    Здравствуйте. У вас в примере кода векторов, который идёт после слов “Запустив эту программу, вы увидите следующее:”, на 5 строке идёт не верный результат.
    Должно же быть: v2 – v1 = (2, -3)

    Вроде мелочь, но в глаза бросается)

    1. writer

      Почему, вроде все корректно, у нас есть вектора
      vector v1 = (3, 4)
      vector v2 = (5, 7)
      теперь мы их вычитаем:
      v2-v1=(5-3, 7-4)=(2,3)

  3. Михаил

    Спасибо! Очень интересный урок. Обязательно буду изучать другие уроки. За примеры отдельное спасибо.

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

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