Особенности ООП в Python
Точка входа
Специальная переменная __name__
Слоты в классах
Декоратор override
Проверка отношения классов
Работа с аргументами и параметрами командной строки
Прогрессбар программы
Паттерн проектирования Singleton
Порядок разрешения методов
Деструкторы
Получаем атрибуты объекта
Объект Ellipsis
Not a Number
Нижнее подчеркивание
Что означает self в методах
Дескрипторы
Копируем объекты
Генераторы
Корутины
Параметры *args и **kwargs
ООП: композиция
Абстрактные классы и методы
Одноразовый метод в классе
Геттеры и сеттеры
Время исполнения кода
5.90M
Категория: ПрограммированиеПрограммирование

Особенности_реализации_ООП_в_python

1. Особенности ООП в Python

2. Точка входа

• В некоторых языках программирования есть функции
main(), с которых начинается выполнение программы. Но в
Python весь код исполняется построчно.
• Как вариант, многие пишут основную логику программы в
обычную функцию main(), а вызывают ее только в условии
if __name__ == '__main__'. Здесь стоит объяснить отдельно
про то, что такое __name__.
• Переменная __name__ — это специальная переменная,
которая будет равна '__main__', только если файл
запускается как основная программа. А при импорте в
качестве модуля выставляется равной имени модуля.
• Таким образом, программа корректно заработает, только
если запустить ее напрямую. Если импортировать ее как
модуль в другой скрипт, то условие не сработает.

3. Специальная переменная __name__

• Когда интерпретатор Python читает файл, то сначала он устанавливает
несколько специальных переменных (пример). Одной из таких переменных
является __name__.
• Если скрипт был запущен напрямую, то в переменную присваивается
значение __main__, в случае импорта — название модуля.
• Типичный пример использования такой переменной — создание точки
входа в программу. Про это уже был ранее пост.
• Вообще атрибут __name__ по умолчанию также ставится всем классам и
функциям.

4.

5. Слоты в классах

• По умолчанию в Python в классах используется словарь __dict__ для
хранения атрибутов, который создается по умолчанию при создании
экземпляра класса. Данная особенность позволяет динамически в
рантайме добавлять атрибуты, но от сюда появляются
соответствующие проблемы с производительностью.
• В случаях, когда мы сразу точно знаем все атрибуты, используемые в
классе, мы можем воспользоваться атрибутом __slots__, который
позволяет задать ограниченный список аргументов для класса. В этом
случае словарь __dict__ не будет создаваться, что позволит
сэкономить память и поднять производительность.

6.

7. Декоратор override

• @override используется для переопределения методов в классах-наследниках. Он позволяет
указать, что метод в подклассе переопределяет метод базового класса.
• Это может быть полезно для:
• — Повышения читабельности кода, так как сразу видно, какие методы переопределены.
• — Выявления ошибок: если имя метода в дочернем классе не совпадает с именем в
родительском, будет выдана ошибка.
• — Проверки типов аргументов: декоратор гарантирует, что типы аргументов совпадают с
базовым методом.

8.

9. Проверка отношения классов

• Для того, чтобы проверить отношения двух классов или экземпляров
(является ли класс классом наследником), есть две простые
встроенные функции isinstance(object, classinfo) и issubclass(class,
classinfo).
• instance - возвращает True, если объект является экземпляром класса
либо экземпляром подкласса данного класса.
• issubclass — проверяет является ли класс наследником другого класса.
• Данные функции зачастую применяются в ООП.

10.

11. Работа с аргументами и параметрами командной строки

• В Python для обработки передаваемых аргументов и создания удобного интерфейса
командной строки существует отличный модуль argparse.
• Для начала нужно создать объект парсера ArgumentParser, в который можно добавить
аргументы с необходимыми параметрами с помощью метода add_argument.
• Первым параметром метод add_argument принимает либо имя обязательного
позиционного аргумента, либо список опциональных аргументов (опциональные аргументы
идентифицируются через -). Также у add_argument есть множество необязательных
опциональных параметров для работы с передаваемыми значениями аргумента.
• После добавления всех аргументов их нужно спарсить с помощью метода parse_args. На
выходе получится объект, содержащий все переданные аргументы.

12.

13. Прогрессбар программы

• Модуль tqdm предназначен для быстрого и расширяемого
внедрения индикаторов выполнения (progressbar) во внешние
интерфейсы программ на Python, предоставляя конечным
пользователям визуальную индикацию хода вычислений или
передачи данных.
• Он также будет полезен в целях отладки, как в качестве
инструмента профилирования, так и в качестве способа
отображения информации журнала итеративной задачи.

14.

15. Паттерн проектирования Singleton

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

16.

17. Порядок разрешения методов

• В Python существует так называемый Method Resolution Order (MRO),
или порядок разрешения методов в классе. Всё, что вам нужно знать –
это порядок, в котором Python ищет нужный атрибут или метод.
• Этот порядок можно получить при помощи атрибута __mro__. Он
говорит о том, что если мы в примере выше попробуем обратиться к
атрибуту value, Python будет искать сначала в классе A, далее в B,
затем в C и в самом конце в object.
• Отсюда становится понятно, что артибут первее будет найден именно
в классе B и равен он будет значению 1.

18.

19. Деструкторы

• При уничтожении экземпляра класса в большинстве языков
вызывается специальный метод, который называется деструктор.
• В Python деструктор можно переопределять с помощью магического
метода __del__, который вызовется автоматически при удалении
объекта.
• Вообще деструкторы крайне редко переопределяется в Python, но
полезно знать, что именно эти методы отвечают за очистку при
удалении объекта.

20.

21. Получаем атрибуты объекта

• Функция vars() возвращает все атрибуты переданного параметра,
в качестве которого может быть почти что угодно: модуль, класс,
экземпляр и т. д.
• Проще говоря, vars() возвращает словарь, который хранится в
атрибуте __dict__ у переданного объекта.
• А если ничего не передать в аргументы, то функция вернет
словарь локальных переменных, как и locals()

22.

23. Объект Ellipsis

• В Python есть крайне интересный объект, который обозначается как ...,
то есть многоточие. Этот объект называется Ellipsis, и используется он в
основном как заготовка для чего-то еще не реализованного.
• Применяется он зачастую при работе со срезами в Numpy, но и в
обычном коде его тоже встретить можно. Например, ... периодически
встречается в теле функции в качестве заглушки.
• Если привести его к логическому типу данных, то увидим True — это
важный момент, потому что похожий по своей сути None выдает False.

24.

25. Not a Number

• В модуле math есть особый объект, который называется NaN (Not a
Number).
• Эти объекты NaN не уникальны, и даже не равны самим себе, так что
вы можете иметь несколько подобных объектов, взятых из нескольких
разных источников.
• Например можно создать подобный объект, просто передав строку
'nan' во float. Кстати говоря, это значит что вы можете использовать
NaN в качестве ключа в словаре (хотя мы и не советуем это делать).

26.

27. Нижнее подчеркивание

• В Python имя переменной может состоять из одного подчеркивания: _. Хотя обычно
такие имена не достаточно описательны и не должны использоваться, есть по
крайней мере три случая, когда _ имеет общепринятый смысл.
• Во-первых, _ используется, когда вам нужно придумать имена для значений,
которые вам не нужны – например, в циклах for.
• Во-вторых, интерактивный режим использует _ для хранения результата последнего
выполненного выражения.
• В-третьих, руководство модуля gettext рекомендует псевдоним его функции
gettext() для _(), чтобы минимизировать загромождение вашего кода.

28.

29. Что означает self в методах

• При вызове методов у объектов сам объект передается первым аргументом, если
это не статический метод. И такой аргумент принято называть self, который новички
прописывают в классах, даже не задумываясь о его значении.
• И к счастью, все это происходит автоматически — вручную объект передавать не
надо. Но для того, чтобы понять этот момент лучше, можно вызвать метод
напрямую у класса и явно передать объект (пример на картинке).
• Далее, уже внутри метода можно обращаться к атрибутам и другим методам у
объекта. Для этого он и передается.
• Проще говоря, если откинуть все технические детали, то можно сказать следующее:
self указывает, что мы как бы применяем метод к самому объекту.

30.

31. Дескрипторы

• Дескриптор – это атрибут объекта со “связанным поведением”, то есть такой
атрибут, при доступе к которому его поведение переопределяется методом
протокола дескриптора. Если хотя бы один из этих методов определен в
объекте, то можно сказать, что этот метод – дескриптор.
• Для того, чтобы определить свой собственный дескриптор, обычно
определяют три специальных метода класса __get__, __set__ или __delete__.
После этого можно создать новый класс и в атрибут этого класса записать
объект типа дескриптор.
• У данного объекта будет переопределено поведение при доступе к атрибуту
(__get__), при присваивании значений (__set__) или при удалении
(__delete__).

32.

33. Копируем объекты

• При присваивании переменной значения другой переменной, как
правило, новый объект не создается, а копируется ссылка уже на
существующий.
• Если использовать функцию copy из стандартной библиотеки, то новый
объект будет создан, но его ссылки на другие объекты останутся
такими же.
• В случае с deepcopy произойдет рекурсивное копирование. Например,
при таком копировании списка все его элементы также скопируются
как новые объекты.

34.

35. Генераторы

• Функции-генераторы выглядят как и обычные, но вместо return
содержат выражения с ключевым словом yield для последовательного
генерирования значений.
• Вызов подобной функции вернёт не значение, а объект генератора.
Далее из этого объекта можно получать значения, например, с
помощью функции next или циклом for.
• Если генератору больше нечего возвращать, то будет вызвано
исключение StopIteration. В целом, генератор — это особый, более
изящный случай итератора.

36.

37. Корутины

• Некой противоположностью генераторов являются корутины. Для
примера напишем функцию, которая будет в бесконечном цикле
подставлять значение и выводить строку.
• Обратите внимание на то, как было использовано ключевое слово
yield. При таком написании создаётся не генератор, а корутина, что
позволяет не просто генерировать значения, но и принимать их.
• Функция работает так: при отправке значения через метод send
локальная переменная name принимает его, а далее значение
подставляется в строку и выводится на экран.

38.

39. Параметры *args и **kwargs

• Все хоть раз видели такую запись, и сейчас мы узнаем, что это за
символы. Сообщу сразу, что args и kwargs – общепринятые имена
переменных, а разбирать мы будем звездочки перед ними.
• В примере функция принимает обязательный аргумент value, а
остальных аргументов она как бы не ожидает. В таком случае *args
упаковывает все не именованные аргументы в кортеж, а **kwargs –
все именованные в словарь.
• Конструкция с *args, **kwargs получается достаточно полезной, если
мы не знаем, кто и в каких целях будет использовать нашу функцию. То
есть, мы можем запихнуть в аргументы практически что угодно.

40.

41. ООП: композиция

• Композиция — это отношение, при котором объекты одного класса связаны
с объектами другого. Такой способ организации взаимодействия между
классами также называется ассоциацией.
• Как правило, в этом случае объект одного из классов (в примере выше это
Salary) является полем другого (Employee). Сложного здесь, как вы видите,
ничего нет.
• Ассоциированные объекты зачастую могут циклически ссылаться друг на
друга, что ломает стандартный механизм сборки мусора.
• В таком случае необходимо использовать слабые ссылки из модуля weakref,

42.

43. Абстрактные классы и методы

• В абстрактном классе обычно реализуется общая часть нескольких сущностей или другими словами – абстрактная
сущность.
• Абстрактный метод – это метод, который не имеет своей реализации в базовом классе, и он должен быть реализован в
классе-наследнике.
• Для того, чтобы создать абстрактный класс с абстрактными методами, надо импортировать вспомогательные метакласс
ABCMeta и декоратор abstractmethod из модуля abc.
• Если мы отнаследуем новый класс от абстрактного класса, не переопределив абстрактные методы, в данном случае
method, и попробуем создать экземпляр, то получим исключение TypeError.
• Для того, чтобы код заработал корректно, нам необходимо переопределить все абстрактные методы. То есть по сути еще
раз просто написать метод, но уже в новом классе.
• UPD. В коде опечатка, в классе GoodExample метод должен называться method, а не function.

44.

45. Одноразовый метод в классе

• В случае, если вдруг понадобится в классе функция, которая будет
использоваться всего один раз, после чего будет использоваться
другая функция, можно воспользоваться приемом на картинке.
• Последней строчкой метода call является переопределение самого
этого метода на другой, а именно normal_call. Таким образом,
изначальный код call будет исполнен только один раз.
• Подобный прием будет полезен в тех случаях, когда при самом первом
вызове метода в классе требуются выполнить одноразовые действия,
которые в дальнейшем не нужны.

46.

47. Геттеры и сеттеры

• В объектно-ориентированных языках распространено использование
геттеров и сеттеров для безопасной работы с приватными полями.
Например, в C# для этого есть удобная конструкция { get; set; }.
• В Python геттер реализуется через декоратор @property, а сеттер в
виде @свойство.setter. В примере метод геттера называется age,
поэтому декоратор сеттера – @age.setter.
• Оба метода должны иметь одинаковое название, по которому можно
будет обращаться как к обычному атрибуту.

48.

49. Время исполнения кода

• Иногда приходится выбирать между несколькими вариантами кода, и
часто отталкиваются от его скорости. Пример вычисления времени
исполнения кода мы сейчас и покажем.
• Всё достаточно просто: с помощью модуля time запоминаем
начальное время, выполняем основной код, узнаём конечное время и
просто высчитываем разницу.
• Если будете использовать этот пример, то просто вставьте свой код
вместо комментария.
English     Русский Правила