Курс по Python ООП: stepik.org/a/116336 Узнаете, что такое дескрипторы, их виды: data descriptor и non-data descriptor, отличие и принцип работы. Инфо-сайт: proproprogs.ru/python_oop Telegram-канал: t.me/python_selfedu
Это ж надо было застрять на этой теме, посмотреть несколько раз тут, посмотреть в других местах, поковырять этот дескриптор, чтоб потом в самом-самом конце видео услышать, что на практике это применяется редко :D
Ну дело в том что это вообще типично для программирования. Обычно есть несколько решений со своими достоинствами и недостатками каждое. И если решение выглядит сложным - скорее всего оно редко применяется. Люди ищут другие и мирятся с их недостатками. НО. Никто не сказал что рано или поздно вам не придется писать например библиотеку. Где подобное нужно. Поэтому можно не понимать все тонкости но знать что такое есть к как примерно устроено полезно. Понадобится - вот тогда и разберетесь.
Это используется для сокращения строк кода, как обёртка этих геттеров и сеттеров классом высшего уровня. Можно просто скопировать эти строки кода и в них (методах __get__ и __set__) делать уже что угодно.
Два вечера разбирался в дескрипторах. Пока на бумажках подробно не расписал алгоритм работы со всеми стрелочками и пояснениями, что к чему относится и чем является, не мог разобраться. Просто в голове не помещалась такая громоздкость. Спасибо огромное за подробное и исчерпывающее объяснение столь мозгодробительной темы!
спасибо за урок, хоть на практики и редко применяется, но я всё равно относительно много времени потратил чтоб всё выучить, в любом случаи понимание программирование стало немножко получше благодаря, этому ролику, ещё раз спасибо!
про хожу очень быстро все уроки и забываю лайки проставить ( Обещаю вернусь и все сделаю ) спасибо за урокиБ сейчас трудно найти нормальное объяснение ООП
Сергей , благодарю за предоставленный материал. Тема действительно показалась сложной на первый взгляд и похожа на паззл, но освоить ее вполне возможно. Это еще и оочень сильно напрягает(тренирует) мозги)
Вот уж воистину теория без практики мертва. Только после того, как появился курс на степике и порешал задачки, смог понять эту тему. И действительно громоздко, но ничего сложного.
С версии Python 3.9 декораторы @classmethod и @property можно было использовать вместе для создания "свойств класса". Однако с Python 3.13 эта возможность устарела и больше не поддерживается.
для более корректного отображения сообщения об ошибке можно использовать функцию repr, Например: TypeError(f'Не разрешённый тип данных у объекта - {repr(value)}') Функция repr() принимает объект питона и конвертирует его в строку, т.е. он будет отображаться в выводе также, как и у вас в IDE Её можно применять в связке с eval или exec, ну да там уже сами нагуглите :)
Сергей, спасибо вам огромное за видео! Но скажите, раз мы имеем такой инструмент как дескриптор, то когда мы все же используем сеттеры и геттеры, когда объекты класса property()? Спасибо!
Здесь все зависит от удобства создания программ, т.е. от задачи. Если нужно реализовать что-то вроде свойств (property) и их много, но логика работы одинаковая, то лучше посмотреть в сторону дескрипторов. То есть, все это инструменты сокращения кода и избежать его дублирования.
Приветствую 🖖 Отличный у вас канал, столько полезной инфы, а главное все понятно. Но вот не нашел у вас ответа, и в интернете не смог найти. Если у меня асинхронно запущен loop, который исполняет какие-то действия до тех пор пока его не отключат. Отключаю его я комбинацией ctrl+c. Есть ли возможность передать данные в запущенный loop извне? Например передать ему команду stop()
Добрый день, а можете прокомментировать на 16:00, почему обращение через getattr и setattr более корректно с точки зрения python? Почему не стоит обращаться через коллекцию?
Сергей, спасибо! Только не совсем понятно, когда мы прописываем x=Integer() внутри класса Point3D, каким образом присваивается значения свойств instance, owner, value. Ведь у класса Integer мы не прописали конструктор init, и в явном виде в выражении x=Integer() мы не передаем никаких параметров
Методы __set__, __get__ и прочие - это магические методы, т.е. они автоматически срабатывают в определенных ситуациях. Вызов прописывается либо на уровне стандартных базовых классов (для некоторых), либо делается на уровне самого интерпретатора языка. Дескрипторы отрабатывают на уровне интерпретатора и значения в параметры подставляются автоматически именно им. Отсюда и вся магия )
@@selfedu_rus Я тоже попался на этом моменте. В самом магическом методе __set_name__ нет описания, поэтому каким образом х из х = Class() попадает в self.name непонятно. Очень глубоко.
там ошибка, на 18 минуте говорите создаём дескриптор не данных ,а 19ю40 говорите сделаем из ред Х дескриптор не данных снова, хотя делаете дискриптор данных наоборот
4:48 - Почему на картинке "делитер" это __del__(self)? Наверное всё-таки должен быть def __delete__(self, instance). И тогда должно выглядить так: def __delete__(self, instance): delattr(instance, self.name)
А как можно реализовать код из прошлого урока с использованием геттеров и сеттеров, если там каждый параметр экземпляра класса имеет собственные методы обработки (фио, возраст, номер паспорта, вес)? Ведь в дескрипторе лишь один универсальный сеттер. Значит ли это, что дескрипторы подходят только для работы с параметрами экземпляра класса, которые имеют единственный и универсальный метод обработки (например, координаты точек должны иметь целочисленное значение и т.д.)?
@selfedu_rus Является ли нормальной практикой использование одного дескриптора данных для нескольких классов? Например, у нас есть классы Picture, Mummies, Papyrus, и мы хотим проверить каждый локальный атрибут экземпляра класса на соответствие строковому типу данных перед присвоением значения атрибуту. Можем ли мы создать один и тот же дескриптор данных (например, StringValue) для всех них? Спасибо.
спасибо за уроки, Сергей! просьба прояснить - в методе __set_name__ мы свойству name дескрипторов присваивали имя c подчеркиванием (_x). 1) при такой записи можно получить доступ к значению как через pt._x, так и через pt.x. понятно, что при обращении pt.x срабатывает геттер дескриптора, но как организовывается доступ при записи pt._x дескриптор понимает обращение к себе как по прямому имени(x), так и по имени, указанному в self.name (_x)? 2) если это сделано, чтобы показать, что x, y, z - защищенные (protected), не будет ли правильным тогда их так и назвать _x = Integer() а в __set_name__ оставить self.name = name. При такой записи, получить доступ через pt.x не получается..
Да, все верно! Здесь дескриптор лишь создает локальный атрибут в объекте Point3D и позволяет управлять с ним через объект класса Integer. Так принято делать и цели что-то жестко запретить программисту здесь нет. Одно нижнее подчеркивание лишь показывает, что переменная внутренняя, защищенная и если программист будет обращаться к ней напрямую, то могут быть проблемы. Если он все же это делает, то, как говорится, его проблемы, его предупреждали ))
Хороший вопрос! Единственное при обращение к pt._x мы обращаемся к локальному свойству экземпляра класса Point3D, поэтому никакого взаимодействия с дескриптором не происходит, если я правильно понял первый вопрос. Во втором случае мы можем безопасно работать с локальными свойствами экземпляра класса Point3D только через дескриптор pt.x, а в случае прямого обращения к приватному свойству pt._x, то возникают риски из-за обращения к приватному свойству вне класса. Сергей, поправьте, если я в чем-то ошибся.
Здравствуйте! Спасибо за Ваши видео, очень познавательно и полезно. Есть вопрос: если из метода дескриптора __set_name__ убрать "_" в строке self.name = "_" + name (то есть оставить self.name = name), то выпадает исключение о достижении лимита глубины рекурсии. Почему? UPD: Кажется, разобрался: При создании экземпляра без "_" получается рекурсивная ссылка внутри класса Point3D: метод __set_name__ бесконечно вызывает х, а х ссылается на Integer(). Честно говоря конкатенация в данном случае выглядит как костыль. Есть-ли способ сделать чуть опрятнее?
Не совсем понял при каких условиях срабатывают магические методы __set_name__, __get__, __set__ в дескрипторе. Мои предположения по поводу их работы, поправьте, если я что-то не так понял: __set_name__ выполняется после создания экземпляра класса дескриптора (например x = Integer())? То есть, он как инициализатор для обычного класса работает. Но почему тогда мы явно не передаём ему параметры owner и name? (есть предположение, в виду того, что вызов идёт непосредственно из класса (Point3D), то передача owner не нужна; а вместо name в данном случае подставляется x). Напомню, что owner - это ссылка на сам класс Point3D Далее, во время инициализации экземпляра класса Point3D, self.x = x представляет собой не создание атрибута экземпляра класса, а вызов метода __set__ в дескрипторе. В самом методе, кроме self, присутствуют instance и value. Value, как я понял, представляет собой x (= 1), а instance - экземпляр класса Point3D __get__ срабатывает после команды p.x (p = Point3D(1, 2, 3)) и возвращает 1. Мы так же можем получить 1, если напишем p._x. Буду смотреть видео заново
А почему метод verify_coord не статический? Он же не использует ни атрибуты объекта, ни атрибуты класса, а работает только со значениями, переданными ему при вызове, что характерно для статических методов, судя из прошлых уроков
можно же было написать def __setattr__(self,key,value): if type(value) != int: raise TypeError и не пришлось бы использовать дескрипторы, прикиньте, вот это я голова
Сергей также вопрос я с данным способом могу обращаться к атрибуту с режимом доступа private. То есть мне не составляет труда написать print(self._ _x) и мне выдаст ответ. Почему так?
Когда вы в дескрипторе создаете как бы приватную переменную (с двумя подчеркиваниями), то находитесь вне класса, где она формируется, поэтому приватной переменной не получается, а создается некий аналог публичной переменной с двумя подчеркиваниями. В общем, в дескрипторах принято создавать защищенные (protected), а не приватные переменные.
1) 19:40 может все таки превратим в дескриптор данных? А не неданных, мы же все таки добавили сеттер 2) Можно какую то ссылку где мы говорили о функциях getattr и setattr? А то помню только магические методы с таким названиям
каким блин образом в сеттере дескриптора integer подставляется значение для аргумента value, откуда это значение берется? в данном примере сеттер получает значения x y z, которые передаются при создании экземпляра класса Point3D, как?. Где можно прочитать про эту логику дескрипторов?
Как я воспринял данный урок: Так, ну все вы видели солнце и знаете, как на нём можно греться. Но что, если нам нужно наоборот охладиться? Смотрите, берём свой ботинок в правую руку, начинаем решать тригонометрические уравнения танцуя на одной ноге, и если всё сделали правильно, вы увидите, как солнце достаёт мороженное и передаёт его вам, но так как солнце горячее, мороженное сразу же испаряется, по этому данный способ особо никто не использует, но думаю эта информация вам не помешает и может быть когда-нибудь пригодится.
Возникает логичный вопрос :) если дескрипторы используются на практике нечасто и у нас есть 10 локальных свойств и не хочется (не можется) писать дескрипторы, то придется писать 30 методов (геттер, сеттер, делитер)?
Можно даже их вынести в отдельный базовый класс (миксин) и подключать по мере необходимости. Правда в такой простой реализации этот класс будет жестко связан с набором локальных свойств дочернего класса.
Добрый день хочу спросить почему на 8:13 где вы показываете схему работы дескрипторов в инициализаторе класса Point3D self.x без нижнего подчеркивания как и y и z не могу это понять
Вопрос, почему verify_coord обёрнут декоратором classmethod, а не staticmethod? Ведь в работе этого метода не используется переменная cls и ему не нужен доступ к классу?
А вот такой вопрос, как инициализатор обращается к свойствам класса x = Integer()? Ведь в его первой строчке перед именем 'x' стоит self, а ведь self тем самым формирует локальное свойство x со значением аргумента x, и никакого вызова или обращения к свойству класса x = Integer нет мы ведь не пишем Point3D.x(непосредственное обращение к свойству)
если я правильно понимаю, то в методе __get__ будет выполняться type(instance) == owner, но тогда непонятно зачем передавать owner в качестве параметра. Единственное что приходит в голову - при вызове дескриптора из класса owner непосредственно, без создания instance. Типа Point3D.x = 10. Или я что-то напутал
подскажи пожалуйста, не могу понять несколько вещей. 1. как работают функции в классе Integer, если мы их не вызывали. 2. зачем в __set_name__ и в классе __get__ нам owner, если мы его не используем?
функции вызываются автоматически - это магические методы, а параметр owner - это синтаксис магических методов, здесь мы его не используем, а в других программах можем использовать
10:19 я скорее всего неправильно понял, но мы сначала создаём локальные атрибуты pt.x = 1, а потом меняем их на _x = 1. Если я ошибаюсь объясните этот момент, пожалуйста
с 6:20 вы говорите что self - это ссылка на создаваемый экземпляр класса, а стрелочку ведете на атрибут 'x' класса 'Point3D'. Можете пояснить, что не так?
Подскажите как правильно прописать синтаксис в def __set_name__(self, owner, name) для приватного атрибута с двумя подчеркиваниями? Просто записать вместо 1 - 2 нижних подчеркивания не подходит "__" + name
Привет, Сергей! Можешь, пожалуйста, более детально объяснить, когда применяется декоратор @classmethod? Например, по твоей функции verify_coord видно, что она не использует атрибуты класса и даже атрибуты самого экземпляра, то есть нет необходимости ссылаться на какой-либо объект. Это больше похоже на вспомогательную функцию, к которой нужно применить декоратор @staticmethod.
Здесь нет строгих критериев. Я больше интуитивно так сделал, т.к. эта функция для внутренней работы с методами класса. Декоратор @staticmethod по мне лучше применять для вызова функции из вне класса, например, для пересчета координат из одних единиц в другие или что-то подобное, что требуется за пределами класса, но при этом берется функция из области видимости класса. А вообще это через практику постигается. Увидите как другие делают, так и вы будете )
а почему Вы метод verify_coord сделали методом класса? В нем ведь никаких атрибутов класса мы не используем, у нас вообще их нет. Может лучше его сделать статическим? Спасибо за уроки)
так принято делать при реализации дескрипторов. Одно нижнее подчеркивание показывает, что это внутреннее свойство класса и его использовать извне не нужно.
@@selfedu_rus, и зачем вы вводите людей в заблуждение, интересно? Метод __del__ вызывается только в том случае, если он определен; но по умолчанию объект не содержит данный метод, а значит и утверждение ваше содержит только часть правды, а значит, что и в целом является неверным. Оператору del достаточно будет только наличия метода __delete__ для удаления дескриптора. И в конечно счете, комментарий выше больше был о том, что в вашем видео говорится в целом о протоколе дескриптора, но при этом приводится метод, который к данному протоколу никакого отношения не имеет (вместо того, который на самом деле там должен был быть). В данном случае слайд и пояснение к нему совершенно точно содержат ошибку, так что ваш ответ о том, что метод __del__ тоже вызывается при удалении, здесь абсолютно неуместен.
Я правильно понял, что методы __set_name__, __get__, __set__ относятся только к дескрипторам? и именно по их наличию Python считает класс дескриптором?
Спасибо огромное за уроки по питону на степике и за ваши труды. вы очень мне помогли с обучением. Сейчас изучаю питон активно очень. по многу часов в день и просыпаюсь по будильнику и дальше учу.мой интерес к программированию на твердую 4. В целом есть большой интерес к разработке игр, очень большой на уровне фанатизма. Я уже ковырял файлы от майкрафта и террарии. хотел мод свой слепить. но знаний ноль. поэтому начал с питона сейчас. хотя понимаю что он не очень подходит для разработки игр и порой я думаю а туда ли я иду ?.. Но Веб разработка в принципе тоже интересна, и заказов будет явно больше чем на игры. наверно с неё и начну. после обучения основ буду учить django и flask. надеюсь это верное решение. ( хотя здесь выбор видимо между тем что нравится и реально нравится )
Если instance.__dict__[self.name] = value , заменим на setattr(instance, self.name, value) , получается полный аналог, никаких подводных камней вроде нет*?
@@selfedu_rus при использовании getattr, setattr возникает ошибка рекурсии. На Степике вы писали, что они вызывают магические методы, из-за этого все. Не врублюсь, почему у вас это работает, а у меня нет.
На изображении на 17:20 non-data descriptor descriptor это класс содержащий только геттер. На 19:36 класс ReadIntX превращается в non-data descriptor посредством добавления сеттера. В связи с чем, у меня возникает некоторое недопонимание какой дескриптор всё таки является non-data, а какой data. И какой у кого приоритет.
@@selfedu_rus подскажите, пожалуйста, правильно ли я понимаю, что для non-data descriptor нет необходимости в методе __set_name__ ? Т.е. такой дескриптор может иметь только лишь метод возвращающий значение.
Дескриптор - это управляющая структура, а не конкретное значение. То есть, мы его сначала создаем (в целом, как объект), а затем, через этот объект можно назначать и считывать величины, которые будут заноситься в объекты, из которых они вызваны.
@selfedu_rus немного почитал доп контент по дескрипторам, и как понял это инструмент, под капотом питона, который позволяет переопределять поведение атрибутов, доступ к ним и тп на более низком уровне. Этот протокол уже заложен в язык и работает, при объявлении методов и атрибутов, просто можно создать дескриптор с собственным дополнительным функционалом для более тонкой настройки своей программы. Еще если немного изменить код, можно добиться поведения как property(не уверен)?: def __set_name__(self, owner, name): self_name = '_' + owner.__name__ + '__' + name
Торможу видимо, но так и не понял для чего нужен декоратор classmethod в таком коде как на 1.30. Я его убираю, все работает, вместо cls прописываю self и вызываю эту проверку в инициализаторе как в прошлом видео и тоже все работает. Какой имеено функционал дает этот декоратор кто нибудь знает?
У вас self будет ссылаться на класс, а не его экземпляр. Как называть параметр вы сами решаете и то, что поменяли имя с cls на self функциональность самого параметра не меняется - это ссылка на класс при декораторе classmethod.
@@selfedu_rus Спасибо за ответ. На самом деле всё достаточно легко и очевидно. На последних минутах видео общая картина хорошо обрисована, не знаю почему тупанул тогда) Посмотрел уроки дальше, общая картина сформировалась и теперь уже с каким то пониманием смотрю заново. Идёт в разы проще.
Время - 6 м. 27с. Цитата - "второй параметр это ссылка на сам класс Integer" Второй параметр "owner" указывает не на класс Integer, а на класс Point3D, специально проверял, если в __set_name__ добавить print(owner), то выдаст -
@@selfedu_rus Действительно. И ведь пересматривал не один раз, пытаясь вникнуть, а всё равно мимо прошло. Пока вы не сказали, в упор этой надписи не замечал, мозг был сконцентрирован на стрелочках.
Почему при init для self мы берем x из атрибутов класса Point3D, а знака равно из параметров? class Point3D: x = Integer() y = Integer() z = Integer() #Приоритет обращения к локальному св-ву такой же как и к дескриптору неданных xr = ReadIntX() def __init__(self, x, y, z): self.x = x self.y = y self.z = z
@@teacherit5840 так язык устроен, self - ссылка на текущий объект и у дескриптора приоритет выше, чем у атрибута, поэтому когда пишем self.x, то ищется вначале дескриптор x в объекте, а если его там нет, то в классе. Это основы языка Python, если этого не знаете, то см. более ранние занятия, я об этом подробно говорю.