Connect with us

Обучение

Заметки Python #25: Дескрипторы, метаклассы

138

Данная тема считается одной из самых сложных и непонятных для junior-разработчиков.

Поехали!

Дескриптор

Это атрибут объекта с некоторым заскриптованным поведением. При доступе к этому атрибуту его поведение меняется на то, что задано методом дескриптора. Это могут быть встроенные методы __get__ , __set__, __delete__. Для наглядности представим, что у нас есть некий объект example — это экземпляр класса. Чтобы получить значение его атрибута мы пишем обращаемся через этот экземпляр класса: x = example.attribute. Эта конструкция отвечает за метод __get__ (т.е. получаем значение атрибута). Для того, чтобы изменить атрибут, нужно присвоить новое значение example.attribut = ‘newattribut’.

Дескрипторы — это, своего рода, декораторы для атрибутов

Теперь в работу включился метод __set__. Удаление соответственно делается через __delete__. Суть в том, что мы можем перехватить доступ к атрибуту и переопределить его поведение. Это позволит инкапсулировать атрибуты и проверять их значения. Всё это очень сильно смахивает на декораторы, где мы меняли поведение функции. Как это выглядит на практике? Для начала простой пример

Результат:

Хотя скрипт и отработал корректно, но проблема данного примера в том, что атрибуты не проверяются, поэтому стоимость будет неправильной. Чтобы это исправить будем использовать дескрипторы и преобразим наш пример.

 

Результат

Теперь отрицательное значение будет выдавать ошибку. Приложение отказывается работать, если мы стараемся некорректно переопределить значение атрибута. Но и этот код можно упростить, т.к. атрибутов может быть много  и если для каждого писать преобразование в свойство, то код станет некомпактным. Как это исправить?

 

Результат:

Здесь у нас атрибуты класса Whisky попадают в протокол дескриптора, где мы явно определяем их извлечение и присваивание. __get__ — извлечение значения по ключу из словаря атрибутов класса, __set__ — присваивание значения по ключу нужному атрибуту класса.

Данный способ был актуален до версии питона 3.5. Теперь можно использовать другой подход, а если точнее — специальный протокол __set_name__, который позволяет отказаться от передачи значений. Его синтаксис: object.__set_name__(self, owner, name). Дескриптор в этом случае назначается на name (имя атрибута). Как это поможет оптимизировать наш код?

Результат:

На первый взгляд, изменения кажутся незначительными. Однако отсутствие передачи параметров в класс ChangeAttr дает нам возможность вынести дескриптор в отдельный модуль и импортировать его, освобождая основной код

Метаклассы

Это классы, экземпляры которого являются классами. Необходимо запомнить три утверждения:

1. Классы — это тоже объекты.

2. Всё в Python — это объекты.

3. Метаклассы — это создатель класса.

Полная «пищевая» цепочка выглядит как: Метакласс -> Класс -> Объект. Например, у нас есть переменная со значением целого числа —  x = 10. Вывод команды print(type(x)) даст нам закономерный итог: <class ‘int’>, целое число. Но что если далее написать print(type(int))? Что выдаст программа? А выдаст она <class ‘type’>.  Значение type — это и есть метакласс. С помощью функции type также можно создавать собственные классы: SuperClass = type («SuperClass», (object), {whisky = lawsons, size = 0,5L}), где «SuperClass» — это название класса,  (object) — это базовый класс , {whisky = lawsons, size = 0,5L} — атрибуты нового класса


Если мы хотим изменить стандартное поведение класса int, то мы можем создать собственный класс ChangeInt, который будет являться метаклассом, соответственно наследоваться он будет от класса type. Пример:

Результат:

Мы видим, что объект теперь создается нашим метаклассом, который меняет структуру вывода (в нашем случае мы просто добавляем строку). Метакласс берет под контроль управление классами.

В чем разница между метаклассом и наследованием?

Наследование — это работа с атрибутами и методами родителя. Метакласс — это изменение поведение класса. Т.е. наследуя, мы просто используем, а метакласс — изменяем.

Методы метакласса

__prepare__ — возвращает словарь для атрибутов класса (изменяем поведение при добавлении атрибутов в словарь)

__new__ — создание и получение нового класса

__init__ — инициализирует новый класс (можно изменить стандартное поведение)

__call__ — создает и возвращает новый экземпляр класса

Нажмите что бы оставить комментарий

Ответить

Ваш e-mail не будет опубликован.

Лучшие сервисы стриминга музыки в 2019 году

Сервисы

Телевидение Wink Ростелеком: Samsung LG, Sony, Phillips, Android TV

Ростелеком

Ноутбуки Asus не видят жесткий диск. Автоматический вход в BIOS при старте

Гаджеты

autoteka-digital2-vin autoteka-digital2-vin

Проверка авто по Автотеке: может ли она обмануть?

Сервисы

.

Digital2.ru - Тренды, IT, WEB- разработка, Цифровая экономика
Свободное копирование и распространение материалов с сайта Digital2.ru
разрешено только с указанием активной ссылки на Digital2 как на источник.
Данный сайт в ходит структуру медиа группы: Online Payments Group Intellect Organic
Товарный знак: OPGIO
Copyright 2019 © All rights reserved

Connect
Подпишись на нас