def decorator(func):
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
        return wrapper
@decorator
def hello():
    print("Hello!")
hello()
Этот код демонстрирует работу декоратора в Python.
Декоратор decorator принимает функцию func в качестве аргумента и возвращает новую функцию wrapper.
wrapper выполняет следующий код:
func (в данном случае hello)Синтаксис @decorator перед определением функции hello является синтаксическим сахаром, эквивалентным hello = decorator(hello).  Он применяет декоратор к функции hello.
При вызове hello() будет выполнен код функции wrapper, который сначала выведет "Before function call", затем "Hello!", и, наконец, "After function call".
Данный код демонстрирует использование декоратора в Python. Давайте разберем, что происходит:
decorator:
      Функция decorator принимает другую функцию (func) в качестве аргумента и возвращает новую функцию (wrapper). Это основа декоратора.
wrapper:
      
        Функция wrapper содержит логику, которая будет выполняться до и после вызова декорируемой функции.  В данном случае, она выводит "Before function call", затем вызывает исходную функцию func(), и после этого выводит "After function call".
      
hello:
      
        @decorator над определением функции hello эквивалентно записи hello = decorator(hello).  Это означает, что функция hello "заменяется" на функцию wrapper, возвращенную декоратором.
      
hello():
      
        Когда вызывается hello(), на самом деле вызывается wrapper().  wrapper() выполняет свою логику (выводит сообщения "Before..." и "After...") и вызывает исходную функцию hello(), которая выводит "Hello!".
      
При выполнении кода будет выведено следующее:
    
Before function call
Hello!
After function call
    
  
  wrapper имеет доступ к переменным из внешней области видимости (в данном случае, к func), даже после того, как внешняя функция decorator завершила свое выполнение.  Это пример замыкания.