Что такое дескриптор объекта
ООП. Дескрипторы
В этой лекции мы рассмотрим такой важный механизм как дескрипторы, а также разберемся с тем как же устроены методы класса.
Свойства
Перед тем как говорить о дескрипторах давайте еще раз поговорим о свойствах (property). Рассмотрим следующий пример: пусть у нас есть класс «Профиль пользователя», который включает следующие поля: имя, фамилия и дата рождения.
Из примера видно, что, во-первых, возраст пользователя вычисляется при каждом обращении, во-вторых, мы только получаем значение и никогда его не изменяем. Было бы логично, чтобы клиентский код работал с возрастом как с обычным атрибутом (свойством) доступным только для чтения и python предоставляет нам для этого механизм свойств (propertes):
Таким образом, свойства дают нам возможность создавать, аналогично другим языкам программирования (например, Java), сеттеры и геттеры, а также вычисляемые свойства (computed properties):
Чтобы понять как работают свойства необходимо разобраться с дескрипторами.
Дескрипторы
В документации дано следующее определение дескрипторов:
Дескрипторы, которые реализуют только __get__ называются дескрипторами не данных (non-data descriptors), а дескрипторы, которые реализуют __set__ и/или __delete__ называются дескрипторами данных (data descriptors). Рассмотрим следующий пример:
Из примера видно, что при обращении к d1 автоматически был вызван метод __get__ определенный на дескрипторе:
Поэтому теперь должно быть понятно, почему при обращении к d2 мы получили просто экземпляр класса. Порядка разрешения имен атрибутов и методов мы коснемся в следующих лекциях.
В Python дескрипторы используются достаточно часто, в том числе и в самом языке, например, функции это дескрипторы:
Это позволяет автоматически передавать экземпляр класса в качестве первого аргумента ( self ), давайте посмотрим на вызов func_descr_get :
Если obj не был передан, то мы имеем дело с обычной функцией, в противном случае это метод и мы «биндим» объект в качестве первого аргумента. На python реализацию функций можно было бы записать так:
А вот примеры реализаций декораторов @staticmethod и @classmethod:
И наконец реализация @property:
Пример простой ORM можно найти в репозитории с лекциями.
Общие сведения о дескрипторах
Дескрипторы создаются вызовами API и определяют ресурсы.
Данные дескриптора
Дескриптор — это относительно небольшой блок данных, который полностью описывает объект для GPU в непрозрачном формате, характерном для GPU. Существует несколько различных типов дескрипторов — отрисовки целевых представлений (RTVs), представлений трафаретов глубины (DSV), представлений ресурсов шейдера (СРВС), неупорядоченных представлений доступа (уавс), представлений постоянного буфера (КБВС) и проб.
Дескрипторы имеют разный размер в зависимости от оборудования GPU. Можно запросить размер SRV, UAV или CBV, вызвав ID3D12Device:: жетдескрипторхандлеинкрементсизе. Дескрипторы показаны в этой документации как неделимые единицы. Ниже приведен пример.
Дескрипторы создаются вызовами API и будут включать такие сведения, как ресурс и MIP-карты, которые должен содержать дескриптор.
Драйвер не следит за ссылками на дескрипторы и не хранит ссылки на них. это приложение позволяет убедиться в том, что используется правильный тип и что информация актуальна. Существует одно небольшое исключение для этого; драйвер проверяет привязки целевых объектов прорисовки, чтобы обеспечить правильную работу цепочек обмена.
Дескрипторы объектов не обязательно должны освобождаться или освобождаться. Драйверы не присоединяют выделения для создания дескриптора. Дескриптор, однако, может кодировать ссылки на другие выделения, для которых приложение владеет временем существования. Например, дескриптор для SRV должен содержать виртуальный адрес ресурса D3D (например, текстуру), на который ссылается SRV. Это обязанность приложения, чтобы убедиться, что он не использует дескриптор SRV, когда базовый ресурс D3D, от которого он зависит, был уничтожен или изменен (например, объявлен как нерезидентный).
Основной способ использования дескрипторов заключается в помещении их в кучу дескрипторов, которые являются резервными копиями памяти для дескрипторов.
Дескрипторы дескрипторов
Дескриптор дескриптора — это уникальный адрес дескриптора. Он похож на указатель, но является непрозрачным, так как его реализация зависит от оборудования. Дескриптор уникален для кучи дескрипторов, поэтому, например, массив дескрипторов может ссылаться на дескрипторы в нескольких кучах.
Дескрипторы ЦП предназначены для немедленного использования, например для копирования, где должны быть идентифицированы источник и назначение. Сразу после использования (например, вызов ID3D12GraphicsCommandList:: омсетрендертаржетс) они могут быть использованы повторно, или их базовая куча может быть удалена.
Чтобы создать дескриптор дескриптора для запуска кучи, после создания самой кучи дескрипторов вызовите один из следующих методов.
Эти методы возвращают следующие структуры:
Поскольку размер дескрипторов зависит от оборудования, для получения инкремента между каждым дескриптором в куче используется:
Для копирования дескрипторов и передачи дескрипторов в вызовы API можно легко смещать начальное расположение с числом инкрементов. Разыменование маркера необязательно, как если бы он был допустимым указателем ЦП, а также для анализа битов в пределах маркера.
Некоторые вспомогательные структуры были добавлены с элементами инициализации, чтобы упростить управление дескрипторами.
Дескрипторы null
При создании дескрипторов с помощью вызовов API приложения передают значение NULL для указателя ресурса в определении дескриптора, чтобы при доступе к нему не приходилось обращаться к результату, связанному с шейдером.
Оставшаяся часть дескриптора должна быть заполнена как можно больше. Например, в случае с представлениями ресурсов шейдера (СРВС) дескриптор можно использовать для различения типа представления (Texture1D, Texture2D и т. д.). Числовые параметры в дескрипторе представления, например число MIP-карты, должны быть заданы для значений, допустимых для ресурса.
Во многих случаях существует определенное поведение для доступа к несвязанному ресурсу, например СРВС, возвращающего значения по умолчанию. Они будут учитываться при доступе к дескриптору NULL, если тип доступа шейдера совместим с типом дескриптора. Например, если шейдер ожидает Texture2D SRV и обращается к NULL SRV, определенному как Texture1D, поведение не определено и может привести к сбросу устройства.
В сводке, чтобы создать дескриптор null, передайте null параметр предварительного источника при создании представления с такими методами, как креатешадерресаурцевиев. В параметре описания представления пдеск Задайте конфигурацию, которая будет работать, если ресурс не равен null (в противном случае может произойти сбой на определенном оборудовании).
Однако корневые дескрипторы не должны иметь значение null.
На 1 класса оборудовании (см. раздел уровни оборудования), все дескрипторы, которые привязаны (посредством таблиц дескрипторов), должны быть инициализированы либо как реальные дескрипторы, либо как дескрипторы null, даже если нет доступа к оборудованию, в противном случае поведение не определено.
На партнеров оборудовании это относится к привязанным дескрипторам CBV и UAV, но не к дескрипторам SRV.
На Tier3 оборудовании нет ограничений на это, при условии, что неинициализированные дескрипторы никогда не обращаются.
Дескрипторы по умолчанию
Дескрипторы по умолчанию нельзя использовать с представлением структуры ускорения райтраЦинг, так как предоставленный параметр предварительного источника должен иметь значение NULL, а расположение должно передаваться через [D3D12_RAYTRACING_ACCELERATION_STRUCTURE_SRV]/Windows/Win32/API/d3d12/NS-d3d12-d3d12_raytracing_acceleration_structure_srv).
Руководство к дескрипторам
Краткий обзор
В этой статье я расскажу о том, что такое дескрипторы, о протоколе дескрипторов, покажу как вызываются дескрипторы. Опишу создание собственных и исследую несколько встроенных дескрипторов, включая функции, свойства, статические методы и методы класса. С помощью простого приложения покажу, как работает каждый из них, приведу эквиваленты внутренней реализации работы дескрипторов кодом на чистом питоне.
Изучение того, как работают дескрипторы, откроет доступ к большему числу рабочих инструментов, поможет лучше понять как работает питон, и ощутить элегантность его дизайна.
Введение и определения
Протокол дескрипторов
Собственно это всё. Определите любой из этих методов и объект будет считаться дескриптором, и сможет переопределять стандартное поведение, если его будут искать как атрибут.
Дескрипторы данных и не данных отличаются в том, как будет изменено поведение поиска, если в словаре объекта уже есть запись с таким же именем как у дескриптора. Если попадается дескриптор данных, то он вызывается раньше, чем запись из словаря объекта. Если в такой же ситуации окажется дескриптор не данных, то запись из словаря объекта имеет преимущество перед этим дескриптором.
Вызов дескрипторов
Пример дескриптора
Этот простой протокол предоставляет просто увлекательные возможности. Некоторые из них настолько часто используются, что были объединены в отдельные функции. Свойства, связанные и несвязанные методы, статические методы и методы класса — все они основаны на этом протоколе.
Свойства
Вызова property() достаточно, чтобы создать дескриптор данных, который вызывает нужные функции во время доступа к атрибуту. Вот его сигнатура:
В документации показано типичное использование property() для создания управляемого атрибута x :
Вот эквивалент property на чистом питоне, чтобы было понятно как реализовано property() с помощью протокола дескрипторов:
Встроенная реализация property() может помочь, когда существовал интерфейс доступа к атрибуту и произошли какие-то изменения, в результате которых понадобилось вмешательство метода.
Функции и методы
В питоне все объектно-ориентированные возможности реализованы с помощью функционального подхода. Это сделано совсем незаметно с помощью дескрипторов не данных.
С помощью интерпретатора мы можем увидеть как на самом деле работает дескриптор функции:
Вывод интерпретатора подсказывает нам, что связанные и несвязанные методы — это два разных типа. Даже если они могли бы быть реализованы таким образом, на самом деле, реализация PyMethod_Type в файле Objects/classobject.c содержит единственный объект с двумя различными отображениями, которые зависят только от того, есть ли в поле im_self значение или там содержится NULL (C эквивалент значения None ).
Статические методы и методы класса
Дескрипторы не данных предоставляют простой механизм для различных вариантов привязки функций к методам.
Так как staticmethod() возвращает функцию без изменений, то этот пример не удивляет:
Если использовать протокол дескриптора не данных, то на чистом питоне staticmethod() выглядел бы так:
В отличие от статических методов, методы класса подставляют в начало вызова функции ссылку на класс. Формат вызова всегда один и тот же, и не зависит от того, вызываем мы метод через объект или через класс.
Это поведение удобно, когда нашей функции всегда нужна ссылка на класс и ей не нужны данные. Один из способов использования classmethod() — это создание альтернативных конструкторов класса. В питоне 2.3, метод класса dict.fromkeys() создаёт новый словарь из списка ключей. Эквивалент на чистом питоне будет таким:
Теперь новый словарь уникальных ключей можно создать таким образом:
Если использовать протокол дескриптора не данных, то на чистом питоне classmethod() выглядел бы так:
Объекты и их дескрипторы в Windows
Объектом в Windows называется структура данных, которая представляет системный ресурс. Таким ресурсом может быть, например, файл, канал, графический рисунок. Операционные системы Windows предоставляют приложению объекты трех категорий:
□ User (объекты интерфейса пользователя);
□ Graphics Device Interface (объекты интерфейса графических устройств);
□ Kernel (объекты ядра).
Категория User включает объекты, которые используются приложением для интерфейса с пользователем. К таким объектам относятся, например, окна и курсоры. Категория Graphics Device Interface включает объекты, которые используются для вывода информации на графические устройства. К таким объектам относятся, например, кисти и перья. Категория Kernel включает объекты ядра операционной системы Windows. К таким объектам относятся, например, файлы и каналы. При изучении системного программирования подробно рассматриваются только объекты категории Kernel. Объекты двух оставшихся категорий рассматриваются при изучении программирования графических интерфейсов.
Под доступом к объектам понимается возможность приложения выполнять над объектом некоторые функции. Приложение не имеет прямого доступа к объектам, а обращается к ним косвенно. Для этого в операционных системах Windows каждому объекту ставится в соответствие дескриптор (handle).
В Win32 API дескриптор имеет тип handle. Дескриптор объекта представляет собой запись в таблице, которая поддерживается системой и содержит адрес объекта и средства для идентификации типа объекта. Дескрипторы объектов создаются операционной системой и возвращаются функциями Win32 API, которые создают объекты. За редким исключением, эти функции имеют вид createobject, где слово object заменяется именем конкретного объекта. Например, процесс создается при помощи вызова функции createProcess. Как правило, такие функции возвращают дескриптор созданного объекта. Если это значение не равно null (или отрицательному значению), то объект создан успешно.
После завершения работы с объектом его дескриптор нужно закрыть, используя функцию closeHandle, которая имеет следующий прототип:
HANDLE hObject // дескриптор объекта
При успешном завершении функция closeHandle возвращает ненулевое значение, в противном случае — false. Функция closeHandle удаляет дескриптор объекта, но сам объект удаляется не всегда. Дело в том, что в Windows на один и тот же объект могут ссылаться несколько дескрипторов, которые создаются другими функциями для доступа к уже созданному ранее объекту. Функция closeHandle уничтожает объект только в том случае, если на него больше не ссылается ни один дескриптор.
Лекция 8. Управление потоками и процессами
Определение потока
Определение потока тесно связано с последовательностью действий процессора во время исполнения программы. Исполняя программу, процессор последовательно выполняет инструкции программы, иногда осуществляя переходы в зависимости от некоторых условий. Такая последовательность выполнения инструкций программы называется потоком управления внутри программы. Отметим, что поток управления зависит от начального состояния переменных, которые используются в программе. В общем случае различные исходные данные порождают различные потоки управления. Поток управления можно представить как нить в программе, на которую нанизаны инструкции, выполняемые микропроцессором. Поэтому часто поток управления также называется нитью (thread). В русскоязычной литературе за потоком управления закрепилось название поток. Для пояснения понятия потока рассмотрим следующую программу, которая выводит минимальное число из двух целых чисел или сообщение о том, что числа равны.
Что такое дескрипторы и их использование в Python 3.6+
Что такое дескрипторы? Очень частый вопрос на собеседованиях. Сложность вопроса в том что реально в своих проектах почти ни кто не использует дескрипторы. Вы можете проработать все жизнь программистом python и ни разу не задействовать их ни в одном своем проекте. Но при этом вы будете почти постоянно использовать их через подключаемые сторонние библиотеки. Обычно говориться если вы захотели использовать их, остановитесь и лучше подумайте об архитектуре проекта. Возможно существует множество других более простых решений. И в большинстве случаев так и будет, за не большим исключением. Дескрипторы традиционно используются только если вы создаете ORM или новый фреймворк. Поэтому знать о них нужно, но не столько для их использования, а больше для понимания как работает магия python.
В сети можно найти множество статей описывающих что такое дескрипторы. Но большинство их них имеют достаточно перегруженное, сухое и не особо понятное описание. Поэтому я нашел и перевел, как мне кажется неплохое объяснение через использование примеров. Оригинал статьи
Вы видели похожий код или, может быть даже писали что-то подобное?
Этот небольшой фрагмент был частично взят из учебника по популярной ORM библиотеки SQLAlchemy. Подобный код можно встреть наверно в любой ORM в python. А вы когда-нибудь задумывались, почему атрибуты id и name не передаются через метод __init__ и потом не привязываются к экземпляру класса, как это обычно делается в классе. Если да то в этой статье я расскажу, как и зачем это делается.
Это описание можно встретить почти в каждой статье о дескрипторах. Но в реальности оно не особо понятное если читаешь его первый раз.
Попробуем рассказать о дескрипторах чуть проще. В python существует три варианта доступа к атрибуту. Допустим у нас есть атрибут a объекта obj :
Python позволяет перехватить выше упомянутые попытки доступа к атрибуту и переопределить связанное с этим доступом поведение. Это реализуется через механизм протокола дескрипторов.
Зачем нам нужны дексрипторы?
Давайте рассмотрим пример:
Что не так с этим кодов? Если этот код начать использовать, мы столкнемся с проблемой. Наши данные ни как не проверяются. То есть цена (price) и количество (quantity) может принимать любое значение:
Вместо того чтобы использовать методы getter и setter и создавать новое API, давайте используем стандартный декоратор property для проверки значения атрибута quantity:
Как использовать дескрипторы
При использовании дескрипторов наше новое определение класса станет таким:
Обратите внимание на атрибуты класса определенные до метода __init__ Это очень похоже на пример от SQLAlchemy приведенный в начале статьи. Теперь нам нужно создать класс NonNegative и реализовать протокол дескрипторов:
Позже мы увидим, как в Python 3.6+ мы можем избежать текущей избыточности кода.
Избыточности можно было бы избежать в более ранних версиях Python, но я думаю, что для объяснения потребуется слишком много усилий, и цель этого поста не в этом.
Добро пожаловать в Python 3.6+
Давайте используем новый протокол дескрипторов появившийся в Python 3.6:
С этим протоколом, мы можем удалить __init__ и привязать имя атрибута к дескриптору:
Теперь окончательная версия нашего кода:
Заключение
Python — это язык программирования общего назначения. Мне нравится, что он не только обладает очень мощными функционалом, которые очень гибок (например, использование мета-классов), а также имеет высокоуровневое API для решения 99% потребностей (например, те же дескрипторов). Дескрипторы, безусловно, являются хорошим инструментом для привязки поведения к атрибутам. Хотя метаклассы потенциально могут делать то же самое, дескриптор может решить проблему более изящно.