#45. Введение в декораторы функций | Python для начинающих

Поділитися
Вставка
  • Опубліковано 29 лис 2024

КОМЕНТАРІ • 111

  • @nikitaaspaev1638
    @nikitaaspaev1638 Рік тому +24

    Посмотрел сначала видео Гоши Дударя. Ничерта не понял. Потом посмотрел ваше видео - разница в преподавании колоссальна! Вы просто безупречно, на простых примерах, объясняете тему, спасибо вам!

    • @Himera1983
      @Himera1983 Місяць тому

      @@nikitaaspaev1638 гоша идиот. Сам себе объясняет

    • @freedomtv2295
      @freedomtv2295 22 дні тому +2

      Гоша и сам ничего не понимает)

  • @onemasterlomaster1829
    @onemasterlomaster1829 3 роки тому +16

    selfedu
    *Благодаря замыканию, у меня аж мозг замкнуло! Как это все запутано, 7 раз пересмотрел! Спасибо Сергей без вас не разобрался бы!*

  • @SovsemNeProstoy
    @SovsemNeProstoy Рік тому +7

    Как-же вы всё прекрасно разжевали нам, за несколько жалких минут понял то, что не понимал в течении месяца.
    *чувства искренней благодарности*

  • @ПётрГригорьев-т1ь
    @ПётрГригорьев-т1ь 2 роки тому +8

    с четвёртого раза нашёл ролик, который понятно простым языком объясняет, что такое декораторы, спасибо!

  • @ГерманРыков-ъ6в
    @ГерманРыков-ъ6в 2 роки тому +16

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

  • @PavelNebo
    @PavelNebo Рік тому +2

    Я посмотрел с десяток разных объяснений декораторов, но только сейчас я по-настоящему понял эту тему.
    Далеко не у каждого человека есть талант преподавателя, именно этот талант позволяет правильно ПРЕПОДАТЬ материал, чтобы он был понятен.
    Спасибо Вам, Сергей !

  • @linked_list_DLL
    @linked_list_DLL Рік тому +2

    Спасибо огромное, автор. Единственный, кто подробно всё объяснил.

    • @андреймарченков-з8п
      @андреймарченков-з8п Рік тому

      @PythonRussian - объясняет просто "жесть", вообще МОЛОДЕЦ парнишка!Посмотри не прогадаешь - объясняет может даже лучьше

  • @ruziliakalyon4168
    @ruziliakalyon4168 2 роки тому +14

    Благодарю вас за обучение! Вы очень внимательный и ответственный учитель. Здоровья Вам и Вашим близким

  • @nedestro
    @nedestro 3 роки тому +6

    Сперва смотрел прошлый вариант видео (от 16 мар. 2020) и там к середине мозги начали заворачиваться, я несколько раз на паузу ставил, отматывал, потом просто досмотрел с чувством "пока рановато, позже обязательно раскурю". А здесь всё с ходу понятно! Декоратор был написан буквально "с полпинка", сижу довольный как слон )) Новая подача зашла шикарно, спасибо огромное!

  • @DobroDelo
    @DobroDelo 2 роки тому +4

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

  • @bvkiposs
    @bvkiposs Рік тому +5

    пожалуй это самая сложная тема после рекурсии, с вашими уроками хоть как-то начал ее понимать)

  • @Арина-ч4с1я
    @Арина-ч4с1я Рік тому +5

    Все очень понятно изложено, спасибо! Теперь не понимаю, как я могла этого не понимать))

  • @СергейФролов-ъ5я
    @СергейФролов-ъ5я 3 роки тому +46

    Сергей, спасибо! Курс крутой, возможно когда-нибудь дойдем до асинхронности в Python, был бы рад послушать и изучить в вашей интерпретации информацию про asyncio и т.д.

    • @AZ993k
      @AZ993k 2 роки тому +6

      Да так не то до асинхронности можно дойти, но и до паттернов проектирования на питоне :) Ме, например, очень нравился всегда C#, учить я его пытался в основном самостоятельно (были курсы, но неудачные). Но по сравнению с C# питон заходит намного быстрее. Думаю, дело в преподавателе

  • @Константин-ш9ч5ъ
    @Константин-ш9ч5ъ Рік тому +1

    Хоть и понял со 2 раза, но материал понятен
    Огромное спасибо автору!

  • @tinaanit2965
    @tinaanit2965 7 місяців тому +1

    пришла по рекомендации из какого-то комментария ) и не пожалела , спасибо за ваше объяснений ! Развития вашему каналу🍀

  • @mksmvnv
    @mksmvnv Рік тому +1

    просто идеальное объяснение, спасибо вам большое)

  • @WAW-k9w
    @WAW-k9w Рік тому +1

    Спасибо, это единственное видео где я все понял. Смотрел других, пересматривал по 3 раза но не понял. Большое спасибо автору!

  • @AntonFedorov-o3n
    @AntonFedorov-o3n Рік тому +1

    Отличное описание, автору спасибо!

  • @konstantinsakharov227
    @konstantinsakharov227 2 роки тому +3

    Спасибо, у Вас дар! Видел много курсов, так ясно, наглядно и без лишнего усложнения не объясняет никто на русском. Всего Вам наилучшего, 100500+ в карму =) Это касается всех курсов которые здесь размещены.

  • @ЕвгенийРоманов-к9я

    Спасибо большое вам, очень ценные уроки😊

  • @МаксимГалиев-у7п
    @МаксимГалиев-у7п 2 роки тому +2

    Лучшие объяснения из тех что я видел, хотя я давно пишу на питоне, но именно изучить его захотелось сейчас

  • @likeclockwork9600
    @likeclockwork9600 3 роки тому +12

    Я тут цикл для себя написал, рекомендую ознакомиться.
    while ' Сергей выкладывает видео ':
    like += 1
    comment += 1
    print('Спасибо Сергей')
    else:
    print('Ждем новое видео')

    • @krab9241
      @krab9241 3 роки тому +1

      пока иначе. а так можно было!?

    • @likeclockwork9600
      @likeclockwork9600 3 роки тому

      @@krab9241 да , такое условие есть только у Пайтона. (после цикла можно поставить else)
      Но сам Гвидо говорил, если бы он заново начал делать Python, то не стал бы делать такое условие.

    • @KonstantinYurievich
      @KonstantinYurievich 3 роки тому +5

      @@likeclockwork9600 'else' в 'while' используется в комбинации с 'break', иначе в 'else' нет смысла...

    • @novichok3417
      @novichok3417 9 місяців тому

      Поздравляю вас с говнокодом!
      Создал бесконечный цикл. И зачем то увеличивал переменные like и comment

    • @ЕгорЕгор-н4ь
      @ЕгорЕгор-н4ь 8 місяців тому

      @@novichok3417 +

  • @Ruslan-q9h
    @Ruslan-q9h 2 роки тому +3

    Супер, не знаю кто еще лучше может настолько понятно объяснить. Спасибо тебе большое)

  • @Ybuotue
    @Ybuotue 2 роки тому +1

    Едрён батон! Не один год работал с декораторами (которые художники), и только под конец видео поняв, что такое декораторы в питоне, на меня снизашло озарение, почему они названы так. Думаю если бы раньше связал два этих одинаковых слова, быстрее бы понял суть декорирования в питоне (считай ретушевание/корректировка/украшение фукнции). Вообще сильно помогла ссылка на Замыкания. Без замыканий реально было трудно понять суть и смысл. Спасибо.

  • @ЯсусБебра
    @ЯсусБебра 7 місяців тому +1

    Коммент в поддержку канала!)

  • @vladimirkulakov6126
    @vladimirkulakov6126 3 роки тому +6

    Ура! Декораторы! А ведь именно на этой теме я засыпался на первом собеседовании) маст хев! Спасибо за короткое и понятное видео, Сергей!

    • @ИгорьВоронов-ъ4м
      @ИгорьВоронов-ъ4м 2 роки тому +1

      Сэр, Вы серьёзно? Не зная декораторов уже можно идти на собеседование?? Я думал это самое начало обучения...

    • @vladimirkulakov6126
      @vladimirkulakov6126 2 роки тому +2

      @@ИгорьВоронов-ъ4м собеседование это в том числе опыт, не попробуешь не узнаешь.

  • @nissanskyline7151
    @nissanskyline7151 19 днів тому

    Спасибо, отлиная лекция

  • @Grigorev84
    @Grigorev84 2 роки тому +1

    Очень крутая подача, лучше ещё не видел!

  • @86Blind
    @86Blind 3 роки тому +2

    Огромное спасибо за урок!

  • @Munchen888
    @Munchen888 Рік тому

    def gcd(a, b):
    if a == 0 or b == 0:
    return a + b
    elif a > b:
    return gcd(a % b, b)
    elif b > a:
    return gcd(a, b % a)
    print(gcd(30, 18))
    Нахождение НОД рекурсивно) Чтобы не забывать!

  • @АлексейСелезнев-т2б

    Спасибо! Очень подробно и простым языком, а главное понятно!

  • @rayhon-h8p
    @rayhon-h8p 8 місяців тому +1

    спасибо большое было очень полезно 🥰🥰🥰

  • @andredru4278
    @andredru4278 9 місяців тому +1

    Спасибо. Великолепно. Я понял.

  • @ПростоХристианство-ч8з

    Доброго времени суток, Сергей, заметил, что при задании функции get_fast_nod следующего вида:
    def get_fast_nod(a, b):
    while b:
    a, b = b, a % b
    return a
    она работает аналогично тому определению, которые Вы предоставили в видео. (т.к. по факту, если a < b, то в строчке a, b = b, a % b они просто поменяются местами)

  • @kiber7575
    @kiber7575 8 місяців тому +1

    мега круть про декораторы

  • @MrSasuke1337
    @MrSasuke1337 11 місяців тому +2

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

  • @jamjam3337
    @jamjam3337 Рік тому +1

    спасибо!👏👍

  • @a.osethkin55
    @a.osethkin55 2 роки тому +1

    Спасибо!

  • @drueserick4158
    @drueserick4158 Рік тому +1

    Я понял это намек, я все ловлю на лету... Но чета я совсем... ... Бллагодаря Вам, все лучше :)

  • @bobby_ridge
    @bobby_ridge 8 місяців тому +2

    Доброго времени суток. Спасибо за видео. Вопрос: могу ли я "продекорировать" функцию функцией-декоратором, в которой не замыкания?

  • @illiukhin
    @illiukhin 3 роки тому +2

    спасибо

  • @ayqikz
    @ayqikz Рік тому +1

    Legend

  • @jetbrain9115
    @jetbrain9115 2 роки тому +1

    Спасибо! Суппер!

  • @АртемНиконов-у7я

    Спасибо,Сергей! Видео понятное,но всё же пример с алгоритмом Евклида может запутать!

  • @andruhaz
    @andruhaz 3 роки тому +2

    Видимо всех замыкает на замыканиях)), но объяснение лучше чем у Била Любановича. Например меня заклинило на "КАААК можно передавать функции второе значение, когда ей уже раньше передали значение)) ПОЧЕМУ она его принимает". Видимо нужно еще раз перечитать вложенные функции)). Да и в принципе selfedu классно сделал, что показал, почему замыкание называется замыканием. Сразу практически все стало понятно. Еще стало понятно, что хочу поучить Фласк у selfedu

  • @ИгорьЖуков-ч4б
    @ИгорьЖуков-ч4б 3 роки тому +3

    Урок супер! Всё предельно понятно и примеры хорошие. Спасибо!
    P.S. Интересно почему при первом вызове декарируемой функции get_nod время вывелось 0,0329.. сек, а не 0,0269 сек, как в последствии и перед этим))

    • @selfedu_rus
      @selfedu_rus  3 роки тому +4

      ОС Windows (скорее всего она у вас) - это не система реального времени и программы могут выполняться с разной скоростью

  • @RelaxationsoothingBOX
    @RelaxationsoothingBOX 2 роки тому +1

    Сергей, я правильно понял, что на практике назначение декорирующей функции это расширение возможностей функции, которую мы декорируем т.е. some_func, не трогая саму функцию?
    И как тогда с помощью декорирующей функции поменять, к примеру, переменную которая находится в функции some_func?

    • @selfedu_rus
      @selfedu_rus  2 роки тому

      Да, все верно. Набор параметров декорируемой функции, как правило, остается неизменным, но если хотите что то поменять, то прописывайте у def wrapper() это у будет набор параметров после декорирования.

  • @ДаринаМаринина
    @ДаринаМаринина 2 роки тому +1

    Спасибо большое, все вполне понятно. Я только не поняла, зачем в принципе нужны декораторы. Ну нельзя ли что-то делать, потом вызывать функцию, потом снова что-то делать?

    • @selfedu_rus
      @selfedu_rus  2 роки тому +1

      во фреймворках они везде и всюду )) очень удобная штука, с практикой усвоите этот момент

  • @ibrahimoglu
    @ibrahimoglu 3 роки тому +2

    👍

  • @Pawokify
    @Pawokify Рік тому +1

    Я не понял почему нужно в wrapper тоже прописывать параметр title.. (Если не использовать args)

  • @alexzir
    @alexzir Рік тому

    Подскажите пожалуйста, на Stepik у вас листинги кода прилагаются? И что там дополнительно есть?

    • @selfedu_rus
      @selfedu_rus  Рік тому +1

      дополнительно только практика

  • @impellergimpeller5133
    @impellergimpeller5133 3 роки тому +1

    👍👍👍👍👍

  • @Pvt.Hudson-j1c
    @Pvt.Hudson-j1c 2 роки тому +3

    Видео за 13 минут, разбираюсь что происходит три часа((

  • @guiterenzog2723
    @guiterenzog2723 2 роки тому

    Что по поводу __name__ и __doc__ при использовании нескольких декораторов?

    • @selfedu_rus
      @selfedu_rus  2 роки тому +1

      да все то же самое, так где декорирование функции происходит, сохраняем __name__ и __doc__

  • @СергейСторожук-д7х
    @СергейСторожук-д7х 2 роки тому +1

    Как всё работает понятно. Но зачем нужен wrapper? Если этот же код прописать во внешней функции test_time всё будет работать точно также. Каким образом используется замыкание?

    • @selfedu_rus
      @selfedu_rus  2 роки тому +1

      вы тогда не сможете передавать аргументы функции (без wrapper), точнее передать только один раз

    • @nilmulyashov7274
      @nilmulyashov7274 2 роки тому

      Ютуб не пропускает слишком большой коммент, так что буду частями)
      К примеру, у нас есть вот такие функции:
      def list_by_cycle():
      lst = []
      for i in range(100):
      if i % 2 == 0:
      lst.append(i)
      return lst
      def list_comprehension():
      lst = [i for i in range(100) if i % 2 == 0]
      return lst
      И мы хотим посчитать их время работы, чтоб сравнить, какая быстрее. Это можно сделать так:
      import time
      def list_by_cycle():
      start = time.perf_counter() # Замеряем время до отработки функции
      lst = []
      for i in range(100):
      if i % 2 == 0:
      lst.append(i)
      return lst
      def list_comprehension():
      start = time.perf_counter() # Замеряем время до отработки функции
      lst = [i for i in range(100) if i % 2 == 0]
      print(time.perf_counter() - start) # Считаем, за сколько отработала функция
      return lst

    • @nilmulyashov7274
      @nilmulyashov7274 2 роки тому

      Но при такой записи мы нарушим принцип единой ответственности функций и принцип DRY. Вынесем всю логику со временем в отдельную функцию. Получится вот это:
      import time
      def wrapper(func):
      start = time.perf_counter() # Замеряем время до отработки функции
      res = func()
      print(time.perf_counter() - start) # Считаем, за сколько отработала функция
      return res
      def list_by_cycle():
      lst = []
      for i in range(100):
      if i % 2 == 0:
      lst.append(i)
      return lst
      def list_comprehension():
      lst = [i for i in range(100) if i % 2 == 0]
      return lst
      Тогда мы сможем обернуть наши функции в функцию wrapper вот так:
      # распечатает только время работы функций
      wrapper(list_by_cycle)
      wrapper(list_comprehension)
      # либо так:
      # распечатает время работы функций и сам сгенерированный список
      print(wrapper(list_by_cycle))
      print(wrapper(list_comprehension))

    • @nilmulyashov7274
      @nilmulyashov7274 2 роки тому

      Пока все ок. Так можно. НО! Что если у функций теперь появятся аргументы? Допустим, я хочу сам задавать диапазон в range. Тогда код функций будет выглядеть вот так:
      def list_by_cycle(n):
      lst = []
      for i in range(n):
      if i % 2 == 0:
      lst.append(i)
      return lst
      def list_comprehension(n):
      lst = [i for i in range(n) if i % 2 == 0]
      return lst
      Как быть с функцией wrapper? Ну, сходу напрашивается вот такой вариант (причем, в общем случае придется написать **kwargs вместо n):
      def wrapper(func, n):
      start = time.perf_counter() # Замеряем время до отработки функции
      res = func(n)
      print(time.perf_counter() - start) # Считаем, за сколько отработала функция
      return res
      Поэтому, когда будем делать обертку (т.е. при вызове wrapper), придется передавать туда дополнительный аргумент, и будет уже вот это:
      # распечатает только время работы функций
      wrapper(list_by_cycle, n=100)
      wrapper(list_comprehension, n=100)
      # либо так:
      # распечатает время работы функций и сам сгенерированный список
      print(wrapper(list_by_cycle, n=100))
      print(wrapper(list_comprehension, n=100))

    • @nilmulyashov7274
      @nilmulyashov7274 2 роки тому

      А теперь, что если мы захотим сохранять сгенерированные списки в переменные? Можно сделать так:
      list_by_cycle_r100 = wrapper(list_by_cycle, n=100)
      list_comprehension_r100 = wrapper(list_comprehension, n=100)
      А теперь, что если мне нужно несколько списков в разных диапазонах? Как поступим? Если в лоб делать, то получится вот это:
      list_by_cycle_r100 = wrapper(list_by_cycle, n=100)
      list_comprehension_r100 = wrapper(list_comprehension, n=100)
      list_by_cycle_r1000 = wrapper(list_by_cycle, n=1000)
      list_comprehension_r1000 = wrapper(list_comprehension, n=1000)
      list_by_cycle_r10000 = wrapper(list_by_cycle, n=10000)
      list_comprehension_r10000 = wrapper(list_comprehension, n=10000)
      Что в итоге-то? Для каждого нового диапазона приходится постоянно оборачивать целевую функцию (т.е. постоянно вызывать wrapper, опять нарушение принципа DRY) и плодить новые переменные. Было бы гораздо удобнее, если бы был синтаксис, позволяющий написать сразу вот так (формируем идеальный конечный результат, так сказать):
      list_by_cycle_r100 = list_by_cycle(n=100)
      list_by_cycle_r1000 = list_by_cycle(n=1000)
      list_by_cycle_r10000 = list_by_cycle(n=10000)
      Т.е. чтоб функция работала, как и раньше, но при этом еще и время считалось. Было бы вообще здорово. При этом код целевых функций не меняем - помним про принцип единой ответственности. Что ж, на ум приходит вот такая реализация (обратите внимание - мы хотим вызвать wrapper лишь один раз в самом начале):
      list_by_cycle = wrapper(list_by_cycle)
      list_comprehension = wrapper(list_comprehension)

  • @pokryshkin1973
    @pokryshkin1973 2 роки тому

    Если в классе есть setter для __old, то не разумно ли в методе __init__ прописать self.old = old вместо self.__old = old

    • @selfedu_rus
      @selfedu_rus  2 роки тому

      да, разумно!

    • @pokryshkin1973
      @pokryshkin1973 2 роки тому

      @@selfedu_rus перепутал тему. хотел спросить в ua-cam.com/video/MxviMwbGl3I/v-deo.html&feature=share&EKLEiJECCKjOmKnC5IiRIQ

  • @СанжарАбдуллаев-к8м

    неужели темная тема

  • @ВладиславСветов-с6н

    Этот учитель не понятно объясняет, замудренным языком,Я не понимаю что вы от него так балдеете, может мусолин постоянно

    • @mcgregor9832
      @mcgregor9832 3 місяці тому

      Если последовательно смотреть, то всё понятно. Это не замудрённый язык

  • @АннаЛазаренко-ы9з
    @АннаЛазаренко-ы9з 2 роки тому

    Не совсем понимаю, для чего вообще внутри func_decorator объявляется еще функция wrapper. Почему нельзя просто в func_decorator: что-то делаем до, func(), что-то делаем после

    • @selfedu_rus
      @selfedu_rus  2 роки тому

      wrapper как раз и подменяет прежнюю функцию, добавляя некоторый функционал, без этого декораторы в принципе не работали бы (или было бы просто вызов одной функции через другую в лоб, передавая каждый раз ссылку на функцию + аргументы)

    • @АннаЛазаренко-ы9з
      @АннаЛазаренко-ы9з 2 роки тому

      @@selfedu_rus Спасибо. Кажется немного поняла, посмотрев еще следующее видео.

  • @mrpjetrov378
    @mrpjetrov378 Рік тому +3

    Всем пока, я на завод

  • @ney107-iz6xl
    @ney107-iz6xl Рік тому

    Я декораторы и замыкания понял
    Тому кто не понял пересмотрите материал возобновите в голове и вернитесь сюда я конечно не весь материал а то что чуть-чуть не поняли или забыли

  • @gore_ot_uma166
    @gore_ot_uma166 2 роки тому

    а есть декоратор, который удаляет все декораторы с какой-либо декорированной функции? ведь не всегда нужен функционал декоратора

    • @selfedu_rus
      @selfedu_rus  2 роки тому +1

      оставляете ссылку на прежнюю функцию, а декоратор на другую переменную, когда она будет не нужна, то сборщик мусора автоматически удалит декоратор, а функция останется как была

    • @gore_ot_uma166
      @gore_ot_uma166 2 роки тому

      Не понял, как это сделать?
      Есть код:
      @декоратор
      Def Функция():
      pass
      Как вызвать эту функцию без декоратора, если он нам в данный момент не нужен?

    • @selfedu_rus
      @selfedu_rus  2 роки тому +2

      @@gore_ot_uma166 так вы декоратор применяйте через функцию:
      a = декоратор(Функция)

    • @gore_ot_uma166
      @gore_ot_uma166 2 роки тому +1

      Спасибо, тоже думал про этот вариант.

  • @Himera1983
    @Himera1983 Місяць тому +2

    вы создаете функцию и потом используете ее как декоратор. Но когда мы не создаем ? когда на видео кто то просто пишет декоратор ( будто все знают что он делает ) . Это и отпугивает . Почему никто не понимает что когда пишешь декоратор не догадываешся что никто не знает откуда ты его взял и как узнать что он делает. Но когда ты создал его сам и декорировал какую то функцию это и ОСЕЛ поймет. И мне кажется даже этот прямой намек ни этот канал ни следующие не поймут.

  • @ИльяГовса
    @ИльяГовса Рік тому

    А зачем делать вложенную функцию?? в чем разница такого кода и
    def func1(func):
    делаем что то перд func
    func()
    делаем что то после func

    • @selfedu_rus
      @selfedu_rus  Рік тому +1

      А как вы этой функции будете передавать аргументы? Каждый раз вызывать func1 с первым параметром func? Это не очень красивое решение.

  • @olegbaskakov3399
    @olegbaskakov3399 3 роки тому +1

    Непонятно, зачем нужен wrapper, если и без него весь код можно поместить в func_decorator(func) и вызывать ее самостоятельно.

    • @selfedu_rus
      @selfedu_rus  3 роки тому +1

      вы тогда не сможете декорировать разные функции

    • @СергейСторожук-д7х
      @СергейСторожук-д7х 2 роки тому

      @@selfedu_rus Сергей, я извиняюсь, но всё равно не понятно - в примере функция test_time вызывается последовательно для медленного алгоритма и для быстрого. Если бы у test_time не было wrapper и весь код по замеру времени был написан непосредственно в test_time то все будет работать аналогично. Я проверил) Поэтому смысл wrapper не понятен.

    • @nilmulyashov7274
      @nilmulyashov7274 2 роки тому

      Я тоже задался этим вопросом. Ответил выше.

  • @triviumfan9411
    @triviumfan9411 Місяць тому

    Хоть бы пример нормальный привёл, а не из какой-то зарубежной горе-книги/статьи.

  • @tbassir9076
    @tbassir9076 Рік тому

    Да, сложновато, но понятно, особенно в конце. Вы всё разложили по полочкам, а в конечном итоге главное оказалось @test_time

  • @mrrototo761
    @mrrototo761 2 роки тому

    Спасибо

  • @gayratsaidakhmedov5451
    @gayratsaidakhmedov5451 11 місяців тому +1

    спасибо