Это ж надо было застрять на этой теме, посмотреть несколько раз тут, посмотреть в других местах, поковырять этот дескриптор, чтоб потом в самом-самом конце видео услышать, что на практике это применяется редко :D
Ну дело в том что это вообще типично для программирования. Обычно есть несколько решений со своими достоинствами и недостатками каждое. И если решение выглядит сложным - скорее всего оно редко применяется. Люди ищут другие и мирятся с их недостатками. НО. Никто не сказал что рано или поздно вам не придется писать например библиотеку. Где подобное нужно. Поэтому можно не понимать все тонкости но знать что такое есть к как примерно устроено полезно. Понадобится - вот тогда и разберетесь.
Это используется для сокращения строк кода, как обёртка этих геттеров и сеттеров классом высшего уровня. Можно просто скопировать эти строки кода и в них (методах __get__ и __set__) делать уже что угодно.
спасибо за урок, хоть на практики и редко применяется, но я всё равно относительно много времени потратил чтоб всё выучить, в любом случаи понимание программирование стало немножко получше благодаря, этому ролику, ещё раз спасибо!
Два вечера разбирался в дескрипторах. Пока на бумажках подробно не расписал алгоритм работы со всеми стрелочками и пояснениями, что к чему относится и чем является, не мог разобраться. Просто в голове не помещалась такая громоздкость. Спасибо огромное за подробное и исчерпывающее объяснение столь мозгодробительной темы!
Сергей , благодарю за предоставленный материал. Тема действительно показалась сложной на первый взгляд и похожа на паззл, но освоить ее вполне возможно. Это еще и оочень сильно напрягает(тренирует) мозги)
про хожу очень быстро все уроки и забываю лайки проставить ( Обещаю вернусь и все сделаю ) спасибо за урокиБ сейчас трудно найти нормальное объяснение ООП
Урок #11 = Пройден Что ж...помните я говорил что застрял на 10-м уроке, потому что хотел более детальнее разобраться, и ушло около часа. Так вот...а вот здесь, грубо говоря, я нечерта не понял😂 Единственное что успокаивает, что автор сказал что очень редко применяется на практике. Но все же, завтра на свежею голову еще раз посмотрю объяснение. Это уже дело принципа, понять весь курс, не пропуская. Спасибо за урок :)
для более корректного отображения сообщения об ошибке можно использовать функцию repr, Например: TypeError(f'Не разрешённый тип данных у объекта - {repr(value)}') Функция repr() принимает объект питона и конвертирует его в строку, т.е. он будет отображаться в выводе также, как и у вас в IDE Её можно применять в связке с eval или exec, ну да там уже сами нагуглите :)
Вот уж воистину теория без практики мертва. Только после того, как появился курс на степике и порешал задачки, смог понять эту тему. И действительно громоздко, но ничего сложного.
Как я воспринял данный урок: Так, ну все вы видели солнце и знаете, как на нём можно греться. Но что, если нам нужно наоборот охладиться? Смотрите, берём свой ботинок в правую руку, начинаем решать тригонометрические уравнения танцуя на одной ноге, и если всё сделали правильно, вы увидите, как солнце достаёт мороженное и передаёт его вам, но так как солнце горячее, мороженное сразу же испаряется, по этому данный способ особо никто не использует, но думаю эта информация вам не помешает и может быть когда-нибудь пригодится.
4:48 - Почему на картинке "делитер" это __del__(self)? Наверное всё-таки должен быть def __delete__(self, instance). И тогда должно выглядить так: def __delete__(self, instance): delattr(instance, self.name)
С версии Python 3.9 декораторы @classmethod и @property можно было использовать вместе для создания "свойств класса". Однако с Python 3.13 эта возможность устарела и больше не поддерживается.
Чтобы понять эту тему, надо прочитать определение этого слова, хотя бы из википедии. Без этого трудно осознать, что творится на экране. Незнакомое слово, ассоциаций к нему нет, а значит все объяснение автора приходится цеплять к мысленной конструкции сферического коня в вакуме. И даже повторения не помогут особо, пока мозг не попробует сам придумать, с чем это связать. Предлагаю не мучиться. Вот это слово - Дескри́птор (от лат. descriptor «описывающий») . Запомнили? Далее, это лексическая единица (СЛОВО, СЛОВОСОЧЕТАНИЕ) информационно-поискового языка, служащая для описания основного СМЫСЛового содержания документа, или для поиска. Вот вам и смысловой ряд: ДЕСКРИПТОР - ОПИСЫВАЮЩЕЕ СМЫСЛ СЛОВО, СЛОВОСОЧЕТАНИЕ. Все сразу становиться понятнее: 1. Создаем класс, описывающий логику для доступа к свойствам или для установки нового значения. Типа сеттеров, гетеров, разных проверок и т.д. Что угодно, что является общим для свойств разных объектов. Тут главный момент, что объекты могут принадлежать вообще разным классам. Но они все будут использовать этот класс-дескриптор, который мы создаем на этом шаге, чтобы обрабатывать свои свойства. 2. Создаем любые нужные нам классы. Создаем в них свойства. Смотрим, видим, что некоторые свойства можно обработать, превратив их в дескриптор. Для этого делаем так, чтобы эти свойства стали экземпляром класса-дескриптора: property1 = DescriptorClassName() property2 = DescriptorClassName() Готово! Мы превратили property1 и property2 в дескрипторы нашего класса! 3. А дальше пользуемся созданными классами как планировали. И когда будем обращаться к свойству-дескриптору property1, например так: a = MyClass(5) b = a.property1 то у нас будет вызываться код из класса дескриптора, который обработает цифру 5 так, как это описано в классе дескрипторе. Основная килл-фича тут в том, что это не наследование, а просто такой крутой способ сократить кучу кода. Не зря в примере у автора используется такое название класса-дескриптора, как Integer (это моя догадка). Чтобы у нас аналогия появилась. Вы же используете тип int в разных классах, функциях. А если попробуете поработать с int как со строкой, то получите ошибку. Потому что у int типа есть свои внутренние проверки (как это делает класс-дескриптор). В общем, урок отличный. Большой спасибо автору, особенно за такие "хлебные крошки", скрытые подсказки как названия классов в объяснении.
Не совсем понял при каких условиях срабатывают магические методы __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. Буду смотреть видео заново
Сергей, спасибо! Только не совсем понятно, когда мы прописываем x=Integer() внутри класса Point3D, каким образом присваивается значения свойств instance, owner, value. Ведь у класса Integer мы не прописали конструктор init, и в явном виде в выражении x=Integer() мы не передаем никаких параметров
Методы __set__, __get__ и прочие - это магические методы, т.е. они автоматически срабатывают в определенных ситуациях. Вызов прописывается либо на уровне стандартных базовых классов (для некоторых), либо делается на уровне самого интерпретатора языка. Дескрипторы отрабатывают на уровне интерпретатора и значения в параметры подставляются автоматически именно им. Отсюда и вся магия )
@@selfedu_rus Я тоже попался на этом моменте. В самом магическом методе __set_name__ нет описания, поэтому каким образом х из х = Class() попадает в self.name непонятно. Очень глубоко.
А как можно реализовать код из прошлого урока с использованием геттеров и сеттеров, если там каждый параметр экземпляра класса имеет собственные методы обработки (фио, возраст, номер паспорта, вес)? Ведь в дескрипторе лишь один универсальный сеттер. Значит ли это, что дескрипторы подходят только для работы с параметрами экземпляра класса, которые имеют единственный и универсальный метод обработки (например, координаты точек должны иметь целочисленное значение и т.д.)?
Вопрос, почему verify_coord обёрнут декоратором classmethod, а не staticmethod? Ведь в работе этого метода не используется переменная cls и ему не нужен доступ к классу?
Сергей, спасибо вам огромное за видео! Но скажите, раз мы имеем такой инструмент как дескриптор, то когда мы все же используем сеттеры и геттеры, когда объекты класса property()? Спасибо!
Здесь все зависит от удобства создания программ, т.е. от задачи. Если нужно реализовать что-то вроде свойств (property) и их много, но логика работы одинаковая, то лучше посмотреть в сторону дескрипторов. То есть, все это инструменты сокращения кода и избежать его дублирования.
А почему метод verify_coord не статический? Он же не использует ни атрибуты объекта, ни атрибуты класса, а работает только со значениями, переданными ему при вызове, что характерно для статических методов, судя из прошлых уроков
@selfedu_rus Является ли нормальной практикой использование одного дескриптора данных для нескольких классов? Например, у нас есть классы Picture, Mummies, Papyrus, и мы хотим проверить каждый локальный атрибут экземпляра класса на соответствие строковому типу данных перед присвоением значения атрибуту. Можем ли мы создать один и тот же дескриптор данных (например, StringValue) для всех них? Спасибо.
это пизда, я 3 дня это ковырял, прошёл все стадии. 1) Отрицание 2) Гнев 3) Поиск компромисса (торг) 4) Депрессия 5) Принятия В итоге, кое как разобрался, и в конце ролика слышу, "Ну на деле это применяется довольно редко"))))
спасибо за уроки, Сергей! просьба прояснить - в методе __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, то возникают риски из-за обращения к приватному свойству вне класса. Сергей, поправьте, если я в чем-то ошибся.
А вот такой вопрос, как инициализатор обращается к свойствам класса x = Integer()? Ведь в его первой строчке перед именем 'x' стоит self, а ведь self тем самым формирует локальное свойство x со значением аргумента x, и никакого вызова или обращения к свойству класса x = Integer нет мы ведь не пишем Point3D.x(непосредственное обращение к свойству)
если я правильно понимаю, то в методе __get__ будет выполняться type(instance) == owner, но тогда непонятно зачем передавать owner в качестве параметра. Единственное что приходит в голову - при вызове дескриптора из класса owner непосредственно, без создания instance. Типа Point3D.x = 10. Или я что-то напутал
там ошибка, на 18 минуте говорите создаём дескриптор не данных ,а 19ю40 говорите сделаем из ред Х дескриптор не данных снова, хотя делаете дискриптор данных наоборот
Подскажите как правильно прописать синтаксис в def __set_name__(self, owner, name) для приватного атрибута с двумя подчеркиваниями? Просто записать вместо 1 - 2 нижних подчеркивания не подходит "__" + name
можно же было написать def __setattr__(self,key,value): if type(value) != int: raise TypeError и не пришлось бы использовать дескрипторы, прикиньте, вот это я голова
Добрый день, а можете прокомментировать на 16:00, почему обращение через getattr и setattr более корректно с точки зрения python? Почему не стоит обращаться через коллекцию?
каким блин образом в сеттере дескриптора integer подставляется значение для аргумента value, откуда это значение берется? в данном примере сеттер получает значения x y z, которые передаются при создании экземпляра класса Point3D, как?. Где можно прочитать про эту логику дескрипторов?
@@selfedu_rus, и зачем вы вводите людей в заблуждение, интересно? Метод __del__ вызывается только в том случае, если он определен; но по умолчанию объект не содержит данный метод, а значит и утверждение ваше содержит только часть правды, а значит, что и в целом является неверным. Оператору del достаточно будет только наличия метода __delete__ для удаления дескриптора. И в конечно счете, комментарий выше больше был о том, что в вашем видео говорится в целом о протоколе дескриптора, но при этом приводится метод, который к данному протоколу никакого отношения не имеет (вместо того, который на самом деле там должен был быть). В данном случае слайд и пояснение к нему совершенно точно содержат ошибку, так что ваш ответ о том, что метод __del__ тоже вызывается при удалении, здесь абсолютно неуместен.
Здравствуйте! Спасибо за Ваши видео, очень познавательно и полезно. Есть вопрос: если из метода дескриптора __set_name__ убрать "_" в строке self.name = "_" + name (то есть оставить self.name = name), то выпадает исключение о достижении лимита глубины рекурсии. Почему? UPD: Кажется, разобрался: При создании экземпляра без "_" получается рекурсивная ссылка внутри класса Point3D: метод __set_name__ бесконечно вызывает х, а х ссылается на Integer(). Честно говоря конкатенация в данном случае выглядит как костыль. Есть-ли способ сделать чуть опрятнее?
Сергей также вопрос я с данным способом могу обращаться к атрибуту с режимом доступа private. То есть мне не составляет труда написать print(self._ _x) и мне выдаст ответ. Почему так?
Когда вы в дескрипторе создаете как бы приватную переменную (с двумя подчеркиваниями), то находитесь вне класса, где она формируется, поэтому приватной переменной не получается, а создается некий аналог публичной переменной с двумя подчеркиваниями. В общем, в дескрипторах принято создавать защищенные (protected), а не приватные переменные.
так принято делать при реализации дескрипторов. Одно нижнее подчеркивание показывает, что это внутреннее свойство класса и его использовать извне не нужно.
подскажи пожалуйста, не могу понять несколько вещей. 1. как работают функции в классе Integer, если мы их не вызывали. 2. зачем в __set_name__ и в классе __get__ нам owner, если мы его не используем?
функции вызываются автоматически - это магические методы, а параметр owner - это синтаксис магических методов, здесь мы его не используем, а в других программах можем использовать
Приветствую 🖖 Отличный у вас канал, столько полезной инфы, а главное все понятно. Но вот не нашел у вас ответа, и в интернете не смог найти. Если у меня асинхронно запущен loop, который исполняет какие-то действия до тех пор пока его не отключат. Отключаю его я комбинацией ctrl+c. Есть ли возможность передать данные в запущенный loop извне? Например передать ему команду stop()
с 6:20 вы говорите что self - это ссылка на создаваемый экземпляр класса, а стрелочку ведете на атрибут 'x' класса 'Point3D'. Можете пояснить, что не так?
@selfedu_rus немного почитал доп контент по дескрипторам, и как понял это инструмент, под капотом питона, который позволяет переопределять поведение атрибутов, доступ к ним и тп на более низком уровне. Этот протокол уже заложен в язык и работает, при объявлении методов и атрибутов, просто можно создать дескриптор с собственным дополнительным функционалом для более тонкой настройки своей программы. Еще если немного изменить код, можно добиться поведения как property(не уверен)?: def __set_name__(self, owner, name): self_name = '_' + owner.__name__ + '__' + name
Возникает логичный вопрос :) если дескрипторы используются на практике нечасто и у нас есть 10 локальных свойств и не хочется (не можется) писать дескрипторы, то придется писать 30 методов (геттер, сеттер, делитер)?
Можно даже их вынести в отдельный базовый класс (миксин) и подключать по мере необходимости. Правда в такой простой реализации этот класс будет жестко связан с набором локальных свойств дочернего класса.
Дескриптор - это управляющая структура, а не конкретное значение. То есть, мы его сначала создаем (в целом, как объект), а затем, через этот объект можно назначать и считывать величины, которые будут заноситься в объекты, из которых они вызваны.
Добрый день хочу спросить почему на 8:13 где вы показываете схему работы дескрипторов в инициализаторе класса Point3D self.x без нижнего подчеркивания как и y и z не могу это понять
Если instance.__dict__[self.name] = value , заменим на setattr(instance, self.name, value) , получается полный аналог, никаких подводных камней вроде нет*?
@@selfedu_rus при использовании getattr, setattr возникает ошибка рекурсии. На Степике вы писали, что они вызывают магические методы, из-за этого все. Не врублюсь, почему у вас это работает, а у меня нет.
Спасибо огромное за уроки по питону на степике и за ваши труды. вы очень мне помогли с обучением. Сейчас изучаю питон активно очень. по многу часов в день и просыпаюсь по будильнику и дальше учу.мой интерес к программированию на твердую 4. В целом есть большой интерес к разработке игр, очень большой на уровне фанатизма. Я уже ковырял файлы от майкрафта и террарии. хотел мод свой слепить. но знаний ноль. поэтому начал с питона сейчас. хотя понимаю что он не очень подходит для разработки игр и порой я думаю а туда ли я иду ?.. Но Веб разработка в принципе тоже интересна, и заказов будет явно больше чем на игры. наверно с неё и начну. после обучения основ буду учить django и flask. надеюсь это верное решение. ( хотя здесь выбор видимо между тем что нравится и реально нравится )
Время - 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, если этого не знаете, то см. более ранние занятия, я об этом подробно говорю.
@@selfedu_rus на мой взгляд тоже лучше использовать static, т.к. код метода не связан ни с классом, ни с его экземплярами и незачем тратить лишние ресурсы передавая в метод ссылку на класс или экземпляр
Это ж надо было застрять на этой теме, посмотреть несколько раз тут, посмотреть в других местах, поковырять этот дескриптор, чтоб потом в самом-самом конце видео услышать, что на практике это применяется редко :D
для себя желательно знать, мы же не знаем что в жизни точно пригодится
Привыкай
Ты спас меня от мучения!! Я уже собирался идти по твоему пути хаха))
Ну дело в том что это вообще типично для программирования. Обычно есть несколько решений со своими достоинствами и недостатками каждое. И если решение выглядит сложным - скорее всего оно редко применяется. Люди ищут другие и мирятся с их недостатками. НО. Никто не сказал что рано или поздно вам не придется писать например библиотеку. Где подобное нужно. Поэтому можно не понимать все тонкости но знать что такое есть к как примерно устроено полезно. Понадобится - вот тогда и разберетесь.
Это используется для сокращения строк кода, как обёртка этих геттеров и сеттеров классом высшего уровня. Можно просто скопировать эти строки кода и в них (методах __get__ и __set__) делать уже что угодно.
объяснение на нереально крутом уровне, спасибо огромное Вам, дорогой!
спасибо за урок, хоть на практики и редко применяется, но я всё равно относительно много времени потратил чтоб всё выучить, в любом случаи понимание программирование стало немножко получше благодаря, этому ролику, ещё раз спасибо!
Сергей, спасибо вам огромное! На ваших видео понимаешь лучше, чем на любом платном курсе
Респектище учителю! Посмотрев этот ролик, немного больше понимаешь, благодаря прошлому ролику, но всё же надо ещё раз пройти.
instance - экземпляр (экземпляр класса)
owner - владелец ( класс, от которого был создан экземпляр)
Пасиба!
один из уроков, который чтобы освоить нужно несколько раз смотреть, но объяснение хорошее, спасибо
Два вечера разбирался в дескрипторах. Пока на бумажках подробно не расписал алгоритм работы со всеми стрелочками и пояснениями, что к чему относится и чем является, не мог разобраться. Просто в голове не помещалась такая громоздкость.
Спасибо огромное за подробное и исчерпывающее объяснение столь мозгодробительной темы!
А я больше разбирался...
Спасибо, очень полезная информация и как всегда прекрасное объяснение (всё понятно с первого раза)!
Сергей, великолепное объяснение, спасибо!
Отличный урок!Отличное объяснение темы!
Сергей , благодарю за предоставленный материал. Тема действительно показалась сложной на первый взгляд и похожа на паззл, но освоить ее вполне возможно. Это еще и оочень сильно напрягает(тренирует) мозги)
Крутое обьяснение. Понятно и зачем и как работает. Главное во всех этих ссылках не запутаться.
Легенда, твой курс будут смотреть пока Пайтон существует
Спасибо! Это гениально) Все на самом деле просто и четко)
про хожу очень быстро все уроки и забываю лайки проставить (
Обещаю вернусь и все сделаю )
спасибо за урокиБ сейчас трудно найти нормальное объяснение ООП
Очень подробно объяснили, спасибо Сергей!
Урок #11 = Пройден
Что ж...помните я говорил что застрял на 10-м уроке, потому что хотел более детальнее разобраться, и ушло около часа. Так вот...а вот здесь, грубо говоря, я нечерта не понял😂 Единственное что успокаивает, что автор сказал что очень редко применяется на практике. Но все же, завтра на свежею голову еще раз посмотрю объяснение. Это уже дело принципа, понять весь курс, не пропуская.
Спасибо за урок :)
Ура, мне все стало понятно, со второго раза. Перезагрузка пошла на пользу 😁
даужь, тема и в прям очень сложная. спасибо Вам большое что разжевали ее для нас
Спасибо, посмотрела!
Сегодня пригодилась эта тема, значительно сократила код
для более корректного отображения сообщения об ошибке можно использовать функцию repr,
Например:
TypeError(f'Не разрешённый тип данных у объекта - {repr(value)}')
Функция repr() принимает объект питона и конвертирует его в строку, т.е. он будет отображаться в выводе также, как и у вас в IDE
Её можно применять в связке с eval или exec, ну да там уже сами нагуглите :)
Спасибо, про repr было в магических методах!
Вот уж воистину теория без практики мертва. Только после того, как появился курс на степике и порешал задачки, смог понять эту тему. И действительно громоздко, но ничего сложного.
Решил пересмотреть, хотел убедиться что owner это ссылка на класс где применяется дескриптор. Спасибо за урок.
Спасибо Сергей. Отличная подача материала!
Интересная тема и хорошее объяснение. Спасибо!
хотелось бы узнать как реализовать дескриптор на примере прошлого урока где мы писали 4 проверки для фио, возраста, пасспорта
Я будто выучил древнюю магию
Как я воспринял данный урок:
Так, ну все вы видели солнце и знаете, как на нём можно греться.
Но что, если нам нужно наоборот охладиться?
Смотрите, берём свой ботинок в правую руку, начинаем решать тригонометрические уравнения танцуя на одной ноге, и если всё сделали правильно, вы увидите, как солнце достаёт мороженное и передаёт его вам, но так как солнце горячее, мороженное сразу же испаряется, по этому данный способ особо никто не использует, но думаю эта информация вам не помешает и может быть когда-нибудь пригодится.
АААА б̶у̶д̶у̶ ̶п̶р̶о̶с̶т̶и̶т̶у̶т̶к̶о̶й я смогу понять дускрипторы >,.,
впервые не всёпонял с первого раза
буду возвращаться к теме позднее
4:48 - Почему на картинке "делитер" это __del__(self)?
Наверное всё-таки должен быть def __delete__(self, instance).
И тогда должно выглядить так:
def __delete__(self, instance):
delattr(instance, self.name)
Шикарная тема-жалко что редко на практике пишем дескрипторы))))
А зачем нужен дескриптор данных? Вот можно же прямо считать с экземпляра класса ту или иную переменную
@@Vena_5 почитайте о свойствах. Они под капотом используют дескрипторы. Только последние более продвинутые
@@Developer_python_ , ладно, благодарствую
С версии Python 3.9 декораторы @classmethod и @property можно было использовать вместе для создания "свойств класса". Однако с Python 3.13 эта возможность устарела и больше не поддерживается.
Полезная штука , код сократил 116 строк до 56
Зачем проверять тип параметра внутри метода verify, есть можно использовать hints?
Чтобы понять эту тему, надо прочитать определение этого слова, хотя бы из википедии. Без этого трудно осознать, что творится на экране. Незнакомое слово, ассоциаций к нему нет, а значит все объяснение автора приходится цеплять к мысленной конструкции сферического коня в вакуме. И даже повторения не помогут особо, пока мозг не попробует сам придумать, с чем это связать. Предлагаю не мучиться.
Вот это слово - Дескри́птор (от лат. descriptor «описывающий») . Запомнили? Далее, это лексическая единица (СЛОВО, СЛОВОСОЧЕТАНИЕ) информационно-поискового языка, служащая для описания основного СМЫСЛового содержания документа, или для поиска. Вот вам и смысловой ряд: ДЕСКРИПТОР - ОПИСЫВАЮЩЕЕ СМЫСЛ СЛОВО, СЛОВОСОЧЕТАНИЕ. Все сразу становиться понятнее:
1. Создаем класс, описывающий логику для доступа к свойствам или для установки нового значения. Типа сеттеров, гетеров, разных проверок и т.д. Что угодно, что является общим для свойств разных объектов. Тут главный момент, что объекты могут принадлежать вообще разным классам. Но они все будут использовать этот класс-дескриптор, который мы создаем на этом шаге, чтобы обрабатывать свои свойства.
2. Создаем любые нужные нам классы. Создаем в них свойства. Смотрим, видим, что некоторые свойства можно обработать, превратив их в дескриптор. Для этого делаем так, чтобы эти свойства стали экземпляром класса-дескриптора:
property1 = DescriptorClassName()
property2 = DescriptorClassName()
Готово! Мы превратили property1 и property2 в дескрипторы нашего класса!
3. А дальше пользуемся созданными классами как планировали. И когда будем обращаться к свойству-дескриптору property1, например так:
a = MyClass(5)
b = a.property1
то у нас будет вызываться код из класса дескриптора, который обработает цифру 5 так, как это описано в классе дескрипторе.
Основная килл-фича тут в том, что это не наследование, а просто такой крутой способ сократить кучу кода. Не зря в примере у автора используется такое название класса-дескриптора, как Integer (это моя догадка). Чтобы у нас аналогия появилась. Вы же используете тип int в разных классах, функциях. А если попробуете поработать с int как со строкой, то получите ошибку. Потому что у int типа есть свои внутренние проверки (как это делает класс-дескриптор). В общем, урок отличный. Большой спасибо автору, особенно за такие "хлебные крошки", скрытые подсказки как названия классов в объяснении.
Не совсем понял при каких условиях срабатывают магические методы __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.
Буду смотреть видео заново
Сергей, спасибо! Только не совсем понятно, когда мы прописываем x=Integer() внутри класса Point3D, каким образом присваивается значения свойств instance, owner, value. Ведь у класса Integer мы не прописали конструктор init, и в явном виде в выражении x=Integer() мы не передаем никаких параметров
Методы __set__, __get__ и прочие - это магические методы, т.е. они автоматически срабатывают в определенных ситуациях. Вызов прописывается либо на уровне стандартных базовых классов (для некоторых), либо делается на уровне самого интерпретатора языка. Дескрипторы отрабатывают на уровне интерпретатора и значения в параметры подставляются автоматически именно им. Отсюда и вся магия )
@@selfedu_rus Я тоже попался на этом моменте. В самом магическом методе __set_name__ нет описания, поэтому каким образом х из х = Class() попадает в self.name непонятно. Очень глубоко.
А как можно реализовать код из прошлого урока с использованием геттеров и сеттеров, если там каждый параметр экземпляра класса имеет собственные методы обработки (фио, возраст, номер паспорта, вес)? Ведь в дескрипторе лишь один универсальный сеттер. Значит ли это, что дескрипторы подходят только для работы с параметрами экземпляра класса, которые имеют единственный и универсальный метод обработки (например, координаты точек должны иметь целочисленное значение и т.д.)?
Вопрос, почему verify_coord обёрнут декоратором classmethod, а не staticmethod? Ведь в работе этого метода не используется переменная cls и ему не нужен доступ к классу?
да, здесь вполне подойдет статик
Сергей, спасибо вам огромное за видео!
Но скажите, раз мы имеем такой инструмент как дескриптор, то когда мы все же используем сеттеры и геттеры, когда объекты класса property()?
Спасибо!
Здесь все зависит от удобства создания программ, т.е. от задачи. Если нужно реализовать что-то вроде свойств (property) и их много, но логика работы одинаковая, то лучше посмотреть в сторону дескрипторов. То есть, все это инструменты сокращения кода и избежать его дублирования.
А почему метод verify_coord не статический? Он же не использует ни атрибуты объекта, ни атрибуты класса, а работает только со значениями, переданными ему при вызове, что характерно для статических методов, судя из прошлых уроков
можно и статический сделать
Спасибо!
@selfedu_rus Является ли нормальной практикой использование одного дескриптора данных для нескольких классов?
Например, у нас есть классы Picture, Mummies, Papyrus, и мы хотим проверить каждый локальный атрибут экземпляра класса на соответствие строковому типу данных перед присвоением значения атрибуту.
Можем ли мы создать один и тот же дескриптор данных (например, StringValue) для всех них?
Спасибо.
запоминать 1 число просто, а милион не то-что громоздко, а сложхо
это пизда, я 3 дня это ковырял, прошёл все стадии.
1) Отрицание
2) Гнев
3) Поиск компромисса (торг)
4) Депрессия
5) Принятия
В итоге, кое как разобрался, и в конце ролика слышу, "Ну на деле это применяется довольно редко"))))
Полезно.
Оказывается метод __set_name__ появился в версии 3,6. Удобная штука. Взял на заметку.
Очень прикольно! Но действительно сложно. Особенно, если плохо разбираться в логике интерпретатора Python :(
спасибо за уроки, Сергей!
просьба прояснить - в методе __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, то возникают риски из-за обращения к приватному свойству вне класса.
Сергей, поправьте, если я в чем-то ошибся.
После просмотра, почему-то препода по высшей математике вспомнил... НадА еще разок так пятьдесят посмотреть... Но тонкую нить уловил вроде...
Здравствуйте, извиняюсь за не скромный вопрос. На какой самой высокой должности вам удалось работать? Спасибо за уроки!
руководитель проектов
@@selfedu_rus спасибо)
А вот такой вопрос, как инициализатор обращается к свойствам класса x = Integer()? Ведь в его первой строчке перед именем 'x' стоит self, а ведь self тем самым формирует локальное свойство x со значением аргумента x, и никакого вызова или обращения к свойству класса x = Integer нет мы ведь не пишем Point3D.x(непосредственное обращение к свойству)
Приоритеты разные, у обычных локальных атрибутов - наименьшие, дескрипторы более высокий имеют.
если я правильно понимаю, то в методе __get__ будет выполняться type(instance) == owner, но тогда непонятно зачем передавать owner в качестве параметра. Единственное что приходит в голову - при вызове дескриптора из класса owner непосредственно, без создания instance. Типа Point3D.x = 10. Или я что-то напутал
наверное, на всякий случай, вдруг кому то нужно будет )
там ошибка, на 18 минуте говорите создаём дескриптор не данных ,а 19ю40 говорите сделаем из ред Х дескриптор не данных снова, хотя делаете дискриптор данных наоборот
Подскажите как правильно прописать синтаксис в def __set_name__(self, owner, name) для приватного атрибута с двумя подчеркиваниями? Просто записать вместо 1 - 2 нижних подчеркивания не подходит "__" + name
Не нужно здесь приватный, мы все же извне работаем с атрибутом, а не внутри экземпляра класса.
@@selfedu_rus выходит что дескрипторы нужно использовать только для "protected" атрибутов?
@@ksymbescenny8859 в основном, да, именно такие локальные атрибуты используются
можно же было написать
def __setattr__(self,key,value):
if type(value) != int: raise TypeError
и не пришлось бы использовать дескрипторы, прикиньте, вот это я голова
Добрый день, а можете прокомментировать на 16:00, почему обращение через getattr и setattr более корректно с точки зрения python? Почему не стоит обращаться через коллекцию?
каким блин образом в сеттере дескриптора integer подставляется значение для аргумента value, откуда это значение берется? в данном примере сеттер получает значения x y z, которые передаются при создании экземпляра класса Point3D, как?. Где можно прочитать про эту логику дескрипторов?
В документации питона написано, что при удалении дескриптора вызывается __delete__(self, instance), а не __del__(self), как показано на слайде
ok
@@selfedu_rus, и зачем вы вводите людей в заблуждение, интересно? Метод __del__ вызывается только в том случае, если он определен; но по умолчанию объект не содержит данный метод, а значит и утверждение ваше содержит только часть правды, а значит, что и в целом является неверным. Оператору del достаточно будет только наличия метода __delete__ для удаления дескриптора. И в конечно счете, комментарий выше больше был о том, что в вашем видео говорится в целом о протоколе дескриптора, но при этом приводится метод, который к данному протоколу никакого отношения не имеет (вместо того, который на самом деле там должен был быть). В данном случае слайд и пояснение к нему совершенно точно содержат ошибку, так что ваш ответ о том, что метод __del__ тоже вызывается при удалении, здесь абсолютно неуместен.
Ой, пойдёт! 😄👍
Здравствуйте) подскажите, кто может настроить up-дискриптор для инстаграм????
В сложных кодах такие элементы незаменимы практически
спасибо
Если я правильно понял, метод verify_coord спокойно можно сделать и @staticmethod
Здравствуйте!
Спасибо за Ваши видео, очень познавательно и полезно. Есть вопрос:
если из метода дескриптора __set_name__ убрать "_" в строке self.name = "_" + name (то есть оставить self.name = name), то выпадает исключение о достижении лимита глубины рекурсии. Почему?
UPD:
Кажется, разобрался:
При создании экземпляра без "_" получается рекурсивная ссылка внутри класса Point3D: метод __set_name__ бесконечно вызывает х, а х ссылается на Integer(). Честно говоря конкатенация в данном случае выглядит как костыль. Есть-ли способ сделать чуть опрятнее?
Да, верно поняли!
self ссылка на экземпляр класса integer?
Сергей также вопрос я с данным способом могу обращаться к атрибуту с режимом доступа private. То есть мне не составляет труда написать print(self._ _x) и мне выдаст ответ. Почему так?
Когда вы в дескрипторе создаете как бы приватную переменную (с двумя подчеркиваниями), то находитесь вне класса, где она формируется, поэтому приватной переменной не получается, а создается некий аналог публичной переменной с двумя подчеркиваниями. В общем, в дескрипторах принято создавать защищенные (protected), а не приватные переменные.
Сергей, дайте, пожалуйста, ссылку на практические примеры. Очень нужно закрепить материал.
ссылка под видео, курс на Stepik
Зачем в __set_name__ конкатенация "_" + name? Просто для отображения в коллекции? Или всё-таки это указание на то, что свойство защищено?
так принято делать при реализации дескрипторов. Одно нижнее подчеркивание показывает, что это внутреннее свойство класса и его использовать извне не нужно.
@@selfedu_rus Хорошо, понятно, а вот насчёт двойного подчеркивания "__" - оно используется для обозначения private свойства?
@@sogorich да
@@selfedu_rus Привет, Сергей! Вроде ошибка RecursionError будет, нет? Поэтому одно нижнее подчеркивание ставим, хотя можно что угодно поставить.
эх) посмотрел пару раз, понял для чего нужно, но не разобрался как работает(
Здравствуйте, большое спасибо за уроки! Хотел написать, что ссылка в соответствующем уроке на Вашем сайте ведет не на этот ролик.
Спасибо, поправил!
подскажи пожалуйста, не могу понять несколько вещей.
1. как работают функции в классе Integer, если мы их не вызывали.
2. зачем в __set_name__ и в классе __get__ нам owner, если мы его не используем?
функции вызываются автоматически - это магические методы, а параметр owner - это синтаксис магических методов, здесь мы его не используем, а в других программах можем использовать
можно ли что то передать в дескриптор и изменить поведение сеттера и геттера?
Но разве параметр owner не является ссылкой на класс из которого вызывается дескриптор?
Да, поэтому в видео в кадре написано: "Внимание: owner - ..."
Кстати, а для чего при создании дескрипторов, к их названиям прибавляются нижние подчёркивания? Спасибо Сергей
если этого не сделать, то при записи в переменную (внутри дескриптора) получится рекурсивный вызов (дескриптор будет все время вызываться)
@@selfedu_rus спасибо
а почему в параметрах функции прописан owner, но он нигде не используется?
мы переопределяем функцию, поэтому должны соблюсти ее сигнатуру
Приветствую 🖖
Отличный у вас канал, столько полезной инфы, а главное все понятно. Но вот не нашел у вас ответа, и в интернете не смог найти.
Если у меня асинхронно запущен loop, который исполняет какие-то действия до тех пор пока его не отключат. Отключаю его я комбинацией ctrl+c. Есть ли возможность передать данные в запущенный loop извне? Например передать ему команду stop()
Спасибо! Сам не знаю. Это у ребят надо спросить )
У меня вопрос по обучения. Достаточно смотреть ваши уроки или необходимо все списывать с них?
Это вопрос к Богу )) У каждого все индивидуально!
Перепутал метод __getattr__ с функцией getattr(), чуть мозг не сломал.... =)
с 6:20 вы говорите что self - это ссылка на создаваемый экземпляр класса, а стрелочку ведете на атрибут 'x' класса 'Point3D'. Можете пояснить, что не так?
стрелочку веду к зеленому прямоугольнику - это на рисунке экземпляр класса Integer
а почему если использовать дескриптор который создает приватные атрибуты, к ним можно обращаться на прямую извне? print(p.__x) --> 1
нельзя, вы просто создаете публичный атрибут __x, к приватному __x он не имеет отношения (можете проверить)
@selfedu_rus немного почитал доп контент по дескрипторам, и как понял это инструмент, под капотом питона, который позволяет переопределять поведение атрибутов, доступ к ним и тп на более низком уровне. Этот протокол уже заложен в язык и работает, при объявлении методов и атрибутов, просто можно создать дескриптор с собственным дополнительным функционалом для более тонкой настройки своей программы.
Еще если немного изменить код, можно добиться поведения как property(не уверен)?:
def __set_name__(self, owner, name):
self_name = '_' + owner.__name__ + '__' + name
Спасибо большое за урок, но тема очень сложная. 😔😔
👍
Возникает логичный вопрос :) если дескрипторы используются на практике нечасто и у нас есть 10 локальных свойств и не хочется (не можется) писать дескрипторы, то придется писать 30 методов (геттер, сеттер, делитер)?
Можно даже их вынести в отдельный базовый класс (миксин) и подключать по мере необходимости. Правда в такой простой реализации этот класс будет жестко связан с набором локальных свойств дочернего класса.
Извините за глупый вопрос, но почему мы иницилизируем дескрипторы в классе, а не в объекте?
Дескриптор - это управляющая структура, а не конкретное значение. То есть, мы его сначала создаем (в целом, как объект), а затем, через этот объект можно назначать и считывать величины, которые будут заноситься в объекты, из которых они вызваны.
Добрый день хочу спросить почему на 8:13 где вы показываете схему работы дескрипторов в инициализаторе класса Point3D self.x
без нижнего подчеркивания как и y и z не могу это понять
потому что мы работаем с локальными _x, _y через дескрипторы x, y, которые объявлены как атрибуты класса
Если instance.__dict__[self.name] = value , заменим на setattr(instance, self.name, value) , получается полный аналог, никаких подводных камней вроде нет*?
да, должно быть одно и то же, хотя, если магический метод setattr переопределен могут возникнуть особенности
@@selfedu_rus спасибо
@@selfedu_rus при использовании getattr, setattr возникает ошибка рекурсии. На Степике вы писали, что они вызывают магические методы, из-за этого все. Не врублюсь, почему у вас это работает, а у меня нет.
@@_garik__ тоже самое, решили проблему?
@@Pluxury30 да, просто забил))
почему перезалив? были ошибки?
да, параметр owner неправильно объяснил )
почему xr это дескриптор не данных если помимо гетера есть и другой метод
6:36 в нижнем описании орфографическая ошибка небольшая. Не примите за критику пжл-а
Спасибо огромное за уроки по питону на степике и за ваши труды. вы очень мне помогли с обучением. Сейчас изучаю питон активно очень. по многу часов в день и просыпаюсь по будильнику и дальше учу.мой интерес к программированию на твердую 4. В целом есть большой интерес к разработке игр, очень большой на уровне фанатизма. Я уже ковырял файлы от майкрафта и террарии. хотел мод свой слепить. но знаний ноль. поэтому начал с питона сейчас. хотя понимаю что он не очень подходит для разработки игр и порой я думаю а туда ли я иду ?.. Но Веб разработка в принципе тоже интересна, и заказов будет явно больше чем на игры. наверно с неё и начну. после обучения основ буду учить django и flask. надеюсь это верное решение. ( хотя здесь выбор видимо между тем что нравится и реально нравится )
Как успехи?
интересно
Уроки огонь! Не могу понять, как описать __del__ в самом дескрипторе?
__del__ это финализатор, в дескрипторах используются __delete__
Время - 6 м. 27с.
Цитата - "второй параметр это ссылка на сам класс Integer"
Второй параметр "owner" указывает не на класс Integer, а на класс Point3D, специально проверял, если в __set_name__ добавить print(owner), то выдаст -
я ж там поправил и ниже текст гигантскими буквами про класс Point3D!
@@selfedu_rus Действительно. И ведь пересматривал не один раз, пытаясь вникнуть, а всё равно мимо прошло. Пока вы не сказали, в упор этой надписи не замечал, мозг был сконцентрирован на стрелочках.
можно ли такое повторить с приватными атрибутами?
UPD
Да, можно
А как же суть инкапсуляции с сокрытием реализации сущности?..🥴 вообще в питоне используется закрытая часть классов?..
сслыка на Point3D😁 но всеравно спасибо👍
мда ) ну перезаливать из-за этого не стану )
Почему при 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
не понял вопрос. В init self.x - это дескриптор, а просто x - переданное значение и так далее.
@@selfedu_rus в init self.x - почему именно берется x = Integer() , а не параметр из скобок
@@teacherit5840 так язык устроен, self - ссылка на текущий объект и у дескриптора приоритет выше, чем у атрибута, поэтому когда пишем self.x, то ищется вначале дескриптор x в объекте, а если его там нет, то в классе. Это основы языка Python, если этого не знаете, то см. более ранние занятия, я об этом подробно говорю.
Зачем использовать classmethod для проверки? Можно же использовать staticmethod. PS: спасибо за урок!
ну уж точно не статик, статики прописываю, если нужно через класс вызывать какой-либо метод никак не связанный с данными самого класса
@@selfedu_rus на мой взгляд тоже лучше использовать static, т.к. код метода не связан ни с классом, ни с его экземплярами и незачем тратить лишние ресурсы передавая в метод ссылку на класс или экземпляр