В чем суть декораторов?
В питоне функции и классы - это тоже объекты. У них есть особый синтаксис объявления, после объявления эти объекты доступны по имени как и любой другой объект с именем (переменная).

Лирическое отступление. В питоне переменные работают не так же как в Си. В Си переменная - это ящичек, а значение в нём как бы лежит. В питоне объект - это как чайный пакетик, а имя как ярлычок на ниточке. Можно к одному пакетику несколько ярлычков привязать, то есть несколько имён. В Си это достигается указателями. В питоне, получается, всё "как бы указатели" и разыменовываются они неявно, под капотом. Но сейчас не о том...

Есть такой паттерн проектирования - декоратор. Декоратор - это функция, которой в аргументе передаётся какой-то объект, а на выходе она возвращает другой объект, каким-то образом задекорированный. В реальной жизни декорация объекта - это, скажем, раскрасить ассенизаторную машину под божью коровку или сделать заточку из ложки (есть, по-прежнему, можно, но можно и пырнуть)... Также декоратор может и вовсе подменять объект полностью, к примеру, нам даётся пластилиновая фигурка, а декоратор - это процесс заливки её её гипсом и производство формы под литьё. Пластилин из готовой формы выковыряли и выкинули, а декоратор - это процесс (функция) производства формы из пластилиновой фигурки.

Повторюсь, в общем случае декоратор - это функция, которая либо немного меняет какой-то объект, либо делает на его основе или по его мотивам какой-то новый. Новый при этом может как содержать старый внутри, так и не содержать. Декоратор может и вовсе не трогать сам объект, а возвращать его без изменения, зато регистрировать его в каком-то внешнем списке создавая так называемый сайд-эффект. Примеров можно придумать уйму.

Итак:
my_object_instance = MyClass()
my_object_instance = my_decorator1(my_object_instance)
my_object_instance2 = my_decorator2(my_object_instance)
В этом примере дважды задекорирован объект. Здесь при первом декорировании оригинал нам, судя по коду, не нужен (или декоратор его не менял, смотря что за декоратор, может просто зарегал где-то в списке...), а при втором декорировании нам остаётся доступным и оригинал и задекорированная версия.

Пока всё просто и не ясно причем тут собаки и чем эти декораторы отличаются от обычных функций. На второй вопрос ответ прост -- ничем особенным. Просто мы такой смысл вкладываем в эти функции. Такая абстрактная концепция.

В Питоне есть синтаксический сахар, чтобы декорировать объявления функций и классов.

Иногда хочется наглядно модифицировать или зарегистрировать в каком-то глобальном реестре класс или функцию, но объявление при этом сильно менять не хочется.
Можно так:
my_pretty_functions = {}

def my_deco(decorated_function):
    '''Это декоратор, который не меняет декорируемый объект, 
    лишь регистрирует его в словаре'''
    my_pretty_functions[decorated_function.__name__] = decorated_function
    return decorated_function

def my_function(x):
    return x ** 2

# вот декорирование вручную, без сахара
my_function = my_deco(my_function)

# Но в питоне же есть сахар для этого, и вот альтернативный вариант декорирования:
@my_deco
def my_other_function(x):
    return x ** 3
Эти два способа декорирования работают одинаково, просто "собака" - это синтаксический сахар.

Не все декораторы такие безобидные. Декоратор может вернуть совершенно другую функцию или вообще что угодно. Тогда это что угодно окажется под оригинальным именем функции, а оригинальная функция вовсе может быть потеряна (выброшена) или засунута в новую с помощью так называемого "замыкания".

Но замыкания - это отдельная большая история, а узнать об этом вы сможете, к примеру, на занятиях по питону на otus.ru, где я скоро, надеюсь, стану преподавать=).

Спрашивайте сто не понятно. Я тут не рассмотрел даже малой доли от разных способов применения декораторов в народном хозяйстве. Надо будет состряпать специальный курс про это.
если после
https://habr.com/ru/post/141411/

но так и не понял ничего(
то только так:
https://www.youtube.com/watch?v=iX_z-ePCCWw&t=39s