#12. Магический метод __call__. Функторы и классы-декораторы | ООП Python

Поделиться
HTML-код
  • Опубликовано: 20 сен 2024
  • Курс по Python ООП: stepik.org/a/1...
    Разбираем работу магического метода _call_ на конкретных примерах. Узнаете как с его помощью можно заменять замыкания функций и создавать классы-декораторы функций.
    Инфо-сайт: proproprogs.ru...
    Telegram-канал: t.me/python_se...

Комментарии • 83

  • @gggutya
    @gggutya Год назад +39

    Грустно что в школах нет таких учителей, радостно что они есть на ютубе

    • @Watcherfromfar
      @Watcherfromfar 6 месяцев назад +8

      Я думаю, если бы в школах платили такую же зарплату, что получают только очень хорошие программисты, то Сергей был бы там учителем. А свой собственный курс на степике, был бы неплохой добавкой к этой зарплате.
      Сейчас профессию учителя просто втоптали в грязь. А ведь ее важность не меньше, чем у какого-нибудь врача. Врач жизни спасает. А чтобы стать хорошим врачом - надо выучиться. Хреновый учитель, даже будущего гения так отворожит от своего предмета, что тот до конца жизни будет считать себя бездарем в этой теме.

  • @ЭмметтБраун-у3т
    @ЭмметтБраун-у3т 19 дней назад +1

    Так вот как реализуются классы декораторы - возился с этой тему,неделю,если не больше,и наконец то понял,спасибо тебе !

  • @NAMASTE4815162342108
    @NAMASTE4815162342108 2 года назад +46

    Супер)) Спасибо большое, Сергей) ещё бы обещанный Синглтон через __call__ и __new__ и будет счастье)

    • @ДмитрийМельников-з2г
      @ДмитрийМельников-з2г 2 года назад

      class MetaSingleton(type):
      __instances = None
      def __call__(cls, *args, **kwargs):
      if cls.__instances is None:
      cls.__instances = super().__call__(*args, **kwargs)
      return cls.__instances
      class Database(metaclass=MetaSingleton):
      def __init__(self, user, psw, port):
      self.user = user
      self.psw = psw
      self.port = port
      db1 = Database('root', '123456', 80)
      db2= Database('root2', '456123', 40)
      print(id(db1), id(db2))
      print(db1.__dict__)
      print(db2.__dict__)

  • @bulyanka228
    @bulyanka228 Месяц назад +3

    насколько ты красава просто передать не могу) молодец!😎

  • @user-rp7sg6eo4b
    @user-rp7sg6eo4b 7 месяцев назад +8

    Лайк, но всё же присоединюсь к тем кого начало трясти от косинусов и синусов, нам бы на яблоках понять Сергей... ))

  • @jaksonmillka
    @jaksonmillka День назад +1

    Урок #12 = Пройден
    Спасибо за урок. Пока что понятия не имею, где бы это могло пригодится, но все же, как говорится, лучше знать, чем не знать :)

  • @McClean974
    @McClean974 7 дней назад +1

    Сергей,спасибо большое за урок,но заметил такую штуку,что в следующем уроке вы более понятно обьяснили
    я посмотрел про str,repr,len,abs,а потом снова посмотрел этот урок и понял))

  • @Watcherfromfar
    @Watcherfromfar 6 месяцев назад +4

    Все понял, кроме вычисления самой производной)) Пожалуй, надо будет завтра обновить знание, что же это такое.

  • @gienek_mscislawski
    @gienek_mscislawski 2 года назад +10

    Здорово! Получается намного компактнее, чем написать параметрический декоратор с кучей вложенных функций

  • @KonstantinPrydnikov1
    @KonstantinPrydnikov1 2 года назад +8

    Глубина курса уровня Бог) спасибо за погружение, уже почти нет рыбы, скоро увидим черных курильщиков)

  • @gbo4net
    @gbo4net Год назад +4

    #43. Области видимости переменных. Ключевые слова global и nonlocal | Python для начинающих
    ruclips.net/video/TacyWpUF1Kk/видео.html
    #44. Замыкания в Python | Python для начинающих
    ruclips.net/video/sJF7OMNgLUs/видео.html
    #45. Введение в декораторы функций | Python для начинающих
    ruclips.net/video/v0qZPplzwUQ/видео.html
    #46. Декораторы с параметрами. Сохранение свойств декорируемых функций | Python для начинающих
    ruclips.net/video/bl_CnIVpWmQ/видео.html

    • @igorratnik2357
      @igorratnik2357 Год назад +1

      Тоже сразу вспомнились замыкания

  • @alexhayes4253
    @alexhayes4253 2 года назад +7

    Вот вам для сравнения подхода ООП и функционального.
    Замыкание из урока, реализованное на функциях:
    def strip_chars(chars):
    def stripit(s):
    res = s.strip(chars)
    return res
    return stripit
    s1 = strip_chars(chars = '?:!.; ')
    s2 = strip_chars(' ')
    res = s1(' Hello world! ')
    res2 = s2(' Hello world! ')
    print(res)
    print(res2)
    Декоратор на функциях:
    def derivate(func):
    def wrapper(x, dx=0.0001):
    res = (func(x + dx) - func(x)) / dx
    return res
    return wrapper
    @derivate
    def df_sin(x):
    return math.sin(x)
    #df_sin = derivate(df_sin)
    print(df_sin(math.pi/3))

  • @ЕрвандАгаджанян-в3к

    Божественно! Огромнейшая вам благодарность!!!!!!

  • @Regina_in_youtube
    @Regina_in_youtube 10 месяцев назад

    Сергей, спасибо за объяснение! Вы для меня учитель №1 по Питону (его изучаю). Спасибо за все видео!

  • @dubinin_s
    @dubinin_s 2 года назад +22

    Вторая часть видео не вызывает вопросов, но от первой немного закипает голова. Прошу прощения если мой вопрос глупый, я всего лишь учусь.
    Откуда вызывается метод __call__, если он не определен в классе? Я так понимаю из базового object. Но если мы его переопределим в нашем классе, как у вас показано, то вызывая класс для создания экземпляра, отработает __call__ из класса в котором __new__ нет и соответственно экземпляр не будет создан. Так же Вы говорили, что метод __new__ отработает всегда при создании экземпляра, тогда зачем его вызывать из __call__, как показано у вас в примере?
    Бывают глупые вопросы, ответить на которые сложно и долго, поэтому если об этом можно более подробно прочитать, подскажите где, буду признателен.

    • @selfedu_rus
      @selfedu_rus  2 года назад +21

      Да, у вас хороший вопрос. Здесь есть один нюанс. Когда мы определяем метод __call__, то он вызывается только для экземпляров класса, но не для самого класса. Поэтому, когда мы создаем объекты класса, например, pt = Point(), то здесь вызывается __call__ для класса, который определен в метаклассе type (о метаклассах я еще буду рассказывать). Надеюсь, теперь будет понятнее )

    • @dubinin_s
      @dubinin_s 2 года назад +3

      @@selfedu_rus спасибо огромное, теперь все предельно понятно.

  • @МамонтовОлег-в9о
    @МамонтовОлег-в9о 2 года назад +3

    Спасибо, товарищ Балакирев! лайк

  • @donfedor007
    @donfedor007 2 года назад +6

    Классы декораторы это круто!

  • @СергейЮров-б6е
    @СергейЮров-б6е 2 года назад +2

    Потрясающе доходчиво

  • @vltoropov677
    @vltoropov677 2 года назад +2

    Самое ценное в этом уроке - вспомнил что такое производная). Немного отвлекся, освежил эту, благополучно забытую, математическую тему.
    Еще бы уяснить разницу между производной функции и дифферениалом. По мне, сейчас, так это одно и то же.
    Огромное спасибо Сергею Михайловичу! Изучаю Python пока в качестве хобби. Пробегаю по быстрому оба курса - начальный и ООП. Знакомлюсь пока, где и что лежит.
    90% понятно сразу, потому что очень дельно изложено.
    Знаю, что Python это инструмент к математике, на что явно указывают другие курсы Сергея. Вот потом от них и буду возвращаться к языку и досконально вникать в ньюансы.
    Еще раз спасибо!!!

  • @igorratnik2357
    @igorratnik2357 Год назад

    Офигеть как годно! Спасибище Сергей!!!

  • @sofiipochta
    @sofiipochta 19 дней назад +2

    Спасибо, посмотрела!

  • @abdulloakramov7941
    @abdulloakramov7941 Год назад +1

    Спасибо за отличный урок 🫡

  • @Uniter_ua
    @Uniter_ua Год назад +1

    Я в перше зробив клас декоратор було інтересно!!! Дуже дякую👍

  • @АртемНиконов-у7я
    @АртемНиконов-у7я Год назад +3

    С Сергеем не только питон выучу , но и геометрию подтяну😄

  • @artemliuboshenko6639
    @artemliuboshenko6639 2 года назад +1

    Супер)) Спасибо большое, Сергей) ещё бы обещанный Синглтон через _call_ и _new_ и будет счастье), самостоятельно я в тупике _ нужна помощь !!!! Тоже очень интересует этот вопрос

  • @andredru4278
    @andredru4278 6 месяцев назад +1

    Спасибо. Ого! крутизна!

  • @Tulad
    @Tulad 6 месяцев назад +2

    У вас опечатка - "underscoRe" 😉

    • @selfedu_rus
      @selfedu_rus  6 месяцев назад +1

      есть такое )

  • @fancyseeds
    @fancyseeds 2 месяца назад +2

    Вы очень доходчиво объясняете, но без практического примера, как использовать на практике такой метод, ничего не понятно. Т.е. в теории то оно пронятно, но где это тспользовать можно ?

    • @selfedu_rus
      @selfedu_rus  2 месяца назад

      для этого курс на Stepik - ссылка под видео

  • @OPPACHblu_channel
    @OPPACHblu_channel Год назад +1

    Спасибо за урок

  • @blanky_nap
    @blanky_nap Год назад +2

    Может underscore?

  • @КириллЧе-я5ы
    @КириллЧе-я5ы 8 месяцев назад +1

    Как раз в принципе все функции в питоне - функторы по существу. Чистых функций и переменных в питоне как бы и нет🥴 все объекты, т.е. экземпляры классов

  • @АльМаслахьат
    @АльМаслахьат 9 месяцев назад +2

    разве это не тоже самое: ";hello world! ".strip("?:!,; ")? причем тут замыкание?

  • @jamjam3337
    @jamjam3337 Год назад +1

    Спасибо!

  • @ДарьяСнеговская-ъ4м
    @ДарьяСнеговская-ъ4м Месяц назад +1

    Сергей, (традиционно) большое спасибо за урок! На 01:30 возник вопрос: как можно переопределить метод `__call__` для класса - и вообще, как программа различает методы `__call__` для класса и для объекта класса, если они выглядят одинаково? И сразу же заодно: я попробовала было отыскать метод `__call__` для класса через `cls.__dict__`, но его там не нашлось. Где он лежит в таком случае?

    • @selfedu_rus
      @selfedu_rus  Месяц назад +1

      Спасибо! Все __call__ внутри классов - это для объектов. Если нужен __call__ для класса - это определяется в метаклассе (о них в конце плейлиста). Успехов!

  • @junPY
    @junPY Год назад +1

    От души!

  • @alexlm2598
    @alexlm2598 2 года назад +4

    опечатка небольшая у вас, правильно не underscope, а underscore

    • @selfedu_rus
      @selfedu_rus  2 года назад

      да, заметил уже позже, спасибо! )

  • @Receive_
    @Receive_ 2 года назад +1

    Нужно дополнительное пояснение, что то совсем запутался я. В момент создания экземпляра класса строкой df_sin = Derivate(df_sin) выполняется функция df_sin, так как аргумент func содержит ссылку на функцию df_sin и присваивает значение - sin(x), приватному атрибуту self.__fn, где x это аргумент принимающий значения = (х + dx) и (x) в строке формулы метода __Call__. Строка print(df_sin(math.pi/3) - запускает магический метод __Call__, где аргумент х получает значение - math.pi/3 и выполняется формула, результат которой, посредством команды return возвращается в функцию print, которая выводит результат на экран. Я все правильно понял?

  • @debirtv8267
    @debirtv8267 Месяц назад +1

    Зддраствуйте Сергей Балакирев , Наверное вопрос странный но, когда мы вычисляли производную в методе КАЛЛ какое значение выбиралось для х если у него даже начального значения не было по сравнению с dx? И как работает __fn(x+dx) если __fn вычисляет синус на три

    • @selfedu_rus
      @selfedu_rus  Месяц назад

      def __call__(self, x, dx=0.0001, *args, **kwargs)
      второй параметр x - это значение аргумента (точки) в которой берется (вычисляется) производная

  • @Developer_python_
    @Developer_python_ Год назад

    Вернувся ставити лайки великій людині)

  • @ВладВасильев-л8б
    @ВладВасильев-л8б 2 года назад +1

    Декораторы - это как китайский! Но очень интересно! :)))

  • @mopnrx2012
    @mopnrx2012 7 месяцев назад +1

    Подскажите пожалуйста, почему удаление символов (в 1 примере) происходит только в начале и в конце?

  • @Developer_python_
    @Developer_python_ Год назад

    Крутий урок- розумієш як писати свої кастомні декоратори-класи

  • @ibrahimoglu
    @ibrahimoglu 2 года назад +2

    👍

  • @ДаринаМаринина
    @ДаринаМаринина 2 года назад +1

    Про удаление символов не очень понятно. Повторила в точности программу - мне пишет "Аргумент должен быть строкой".

  • @impellergimpeller5133
    @impellergimpeller5133 2 года назад +1

    👍👍👍👍👍

  • @rogozin_ilya
    @rogozin_ilya 2 года назад +2

    Что быстрее работает? Есть смысл использовать классы вместо декораторов?

    • @selfedu_rus
      @selfedu_rus  2 года назад

      Думаю, разницы в скорости вы здесь не заметите ) А так, что удобнее, то и используйте.

  • @Zavintyshka
    @Zavintyshka Год назад

    Сергей здраствуйте! Скажите пожалуйста правильно ли я понял: сначала имя df_sin ссылается на объект функции в памяти и у этого объекта счетчик ссылок равен 1. Потом когда функция df_sin декорируется, имя df_sin ссылается на экземпляр класса Derivate. Получается счетчик ссылок уменьшается на 1 и мгновенно увеличивается на 1 за счет того, что атрибут __fn у экземпляра df_sin начинает ссылаться на эту область памяти.

    • @selfedu_rus
      @selfedu_rus  Год назад +1

      Команда df_sin = Derivate(df_sin) создает объект класса Derivate и внутри объекта сохраняется ссылка на функцию df_sin. Далее, команда df_sin(math.pi/4) вызывает метод __call__ объекта класса Derivate и выполняется то, что записано внутри этого метода. Все, никаких счетчиков! Подробнее - телеграм-канал по Python.

  • @redvizer8337
    @redvizer8337 Год назад +1

    def __call__(self, mul, *args, **kwargs):
    def wrapper(x, dx=0.0001, *args, **kwargs):
    return (self.func(x + dx) - self.func(x)) / dx * mul
    return wrapper
    вопрос как в эту конструкцию поступает ссылка на функцию в пайчарме все сработало но если вспомнить функции декораторы, то там мы писали во внешней функции ссылку на функцию в качестве параметра здесь же я не понимаю где ссылка или она автоматически туда попадет при вызове __call__?

    • @Piro_
      @Piro_ Год назад

      В методе call есть ссылка self, функция будет искаться в экземпляре класса, в котором определена функция (funk) через self.funk( ) - self тут основное.

  • @ДмитрийМельников-з2г

    А зачем переопределять метод __call__, если мы можем просто не перезаписывать свойства?
    class DataBase:
    __instance = None
    def __new__(self, *args, **kwargs):
    if self.__instance is None:
    self.__instance = super().__new__(self)
    return self.__instance
    def __init__(self, user, psw, port):
    if not hasattr(self, 'user'):
    self.user = user
    if not hasattr(self, 'psw'):
    self.psw = psw
    if not hasattr(self, 'port'):
    self.port = port
    def __del__(self):
    self.__instance = None
    db1 = DataBase('root', '123456', 80)
    db2 = DataBase('root2', '456123', 40)
    print(id(db1), id(db2))
    print(db1.__dict__)
    print(db2.__dict__)

  • @VGCor
    @VGCor Год назад

    Получается декораторы на классах менее гибки, потому что на них можно реализовать лишь двойной уровень вложенности. Чаще, как по мне, применяется тройной уровень вложенности. Да и для понимания тройного уровня вложенности не нужно знание ООП, только понимание областей видимости.

    • @selfedu_rus
      @selfedu_rus  Год назад +1

      на уровне ООП вполне можно делать и декораторы с параметрами, если вы об этом (подробно рассматривается в курсе по ООП на Stepik)

  • @rembomenlee
    @rembomenlee 3 месяца назад +1

    Можно бы попроще объяснять не включая сложные вычисления в код которые отвлекают от сути метода...

  • @teacherit5840
    @teacherit5840 2 года назад

    Подскажите, если __call__ вызывается в момент создания экз класса, и он вызывает __new__ и __init__, то почему если делать пошагово то увидим , что сначала работает __new__ , затем __init__, и только если мы вызываем сам экземпляр то работает __call__

    • @teacherit5840
      @teacherit5840 2 года назад

      #12. Магический метод __call__. Функторы и классы-декораторы
      #dunder - методы от англ. double underscore
      #Магический метод __call__
      '''
      В действительности, когда происходит вызов класса, то автоматически запускается
      магический метод __call__ и в данном случае он создает новый экземпляр этого класса:
      '''
      """
      c = Counter()
      def __call__ (self, *args, **kwargs):
      obj = self._new_(self, *args, **kwargs)
      self._init_(obj, *args, **kwargs)
      return obj
      """
      #Функторы - это классы с определённым оператором(). Мы сможем вызвать экз класса
      class Counter:

      def __init__(self):
      print('вызов метода __init__')
      self.__counter = 0

      def __new__(cls, *args, **kwargs):
      print('вызов метода __new__')
      return super().__new__(cls)

      def __call__(self, step = 1, *args, **kwargs):
      print('вызов метода __call__')
      self.__counter += step
      return self.__counter


      c = Counter()
      c2 = Counter()
      """Благодаря методу __call__, мы можем вызывать экземпляры класса подобно функции"""
      c()
      c(2)
      res = c(10)
      res2 = c2(-5)
      print(res, res2)

    • @selfedu_rus
      @selfedu_rus  2 года назад +1

      здесь метод __call__ вызывается у метакласса, а не тот, что объявлен в классе. Когда мы объявляем __call__ в классе, то он вызывается для объектов этого класса (не при создании, а при вызове их подобно функциям).

  • @ФилиппБезручко-ш3ь
    @ФилиппБезручко-ш3ь 2 года назад +1

    Привет. Делал сайт по твоим видео, решил добавить функцию "Поделиться статьёй", но я потерпел неудачу. При отправке имейла, через send_mail() после "sock.connect(sa)", вылазит эта зараза "ConnectionRefusedError: [WinError 10061] Подключение не установлено, т.к. конечный компьютер отверг запрос на подключение".Весь интернет перерыл. Я могу тебе задонатить сколько скажешь(в пределал разумного).Буду благодарен невероятно!

    • @mountaindeserver
      @mountaindeserver 2 года назад

      Могу попробовать тебе помочь сам. Была такая ошибка у меня

    • @ФилиппБезручко-ш3ь
      @ФилиппБезручко-ш3ь 2 года назад

      @@mountaindeserver буду очень благодарен. Что от меня нужно?

  • @playt_pythonlearn
    @playt_pythonlearn 2 года назад +1

    11:00

  • @stasvolo1180
    @stasvolo1180 9 месяцев назад

    "матч"... Хд

  • @torri11
    @torri11 2 года назад +4

    примеры нужно брать проще. 2+2 .А синусы косинусы тут ни чему. Потому что надо еще вспоминать что это такое.

    • @hjehf9094
      @hjehf9094 2 года назад

      Позор тебе, тут теорию гомотопий надо

  • @nbvfnbvf
    @nbvfnbvf 2 года назад +4

    Спасибо за видео!
    Вот только.
    Match читается "мэтч", а math - "мэз"!

    • @ValentinKurilyuk
      @ValentinKurilyuk Год назад

      Согласен. Только это не урок английского. Я готов и к неидеальной дикции и к "русскому" английскому Сергея, т.к. для объяснения теории Питона это не существенно.

    • @nbvfnbvf
      @nbvfnbvf Год назад

      @@ValentinKurilyuk
      Ну, да.
      Поскольку видео про питон, строго запрещено потратить 10 минут и заглянуть в словарь.
      Видео посмотрят дети, они буду все это повторять.
      Оправдание - оно как ...

  • @АлексейЕгоров-к9е
    @АлексейЕгоров-к9е Год назад +2

    не понимаю, зачем было усложнять пример синусами, пи, импортом math ??? очень отвлекает. Результат 0. и 16 цифр ...... нафиг он нужен? какая-то производная. куча слов которые сходу и не вспомнишь. функция должна быть x * 2 или х + 2!!!! вот и хватит. тут главное декораторы понять, а не вспомнить всю математику.
    p.s.: а на каком уроке объясняли что значит F7 и F8 ??? не понимаю, почему жмется то ф7, то ф8.

    • @user-ie2ey9cs2k
      @user-ie2ey9cs2k 7 месяцев назад

      тебе подробный гайд по блокноту тоже записать?

  • @sonoffjord2773
    @sonoffjord2773 Год назад

    math - мэвс

  • @GetEnjoyChannel
    @GetEnjoyChannel Год назад

    благодаря вам прохожу стажировку в Aston

  • @PavelNebo
    @PavelNebo Год назад +1

    Выглядит намного красивее и компактнее, чем обычный вариант с вложенными функциями!
    Особенно в случае с декоратором с параметрами, где 3 функции вложены друг в друга