Написание декораторов с дополнительной обёрткой и без неё
Есть два декоратора, первый без дополнительной функции, в качестве параметров принимает сразу и функцию, которой мы добавляем дополнительный функционал, и аргументы этой же функции:
def test(*args): #Наша функция, тест, которая будет принимать произвольное количество параметров.
print(*args)
def call_start_end(func, *args):
print('Начало теста')
func(*args)
print('Конец теста')
call_start_end(test, 'Тестируем', 'Ещё не закончили', '...')
# Вывод :
# Начало теста
# Тестируем, Ещё не закончили, ...
# Конец теста
Второй декоратор уже использует дополнительную функцию, чтобы обернуть обёртку:
def test(*args):
print(*args)
def prewrapper(func): #Обёртка для обёртки, которая принимает функцию.
def call_start_end(*args): # Сама обёртка, которая принимает аргументы для функции, функционал которой мы расширяем.
print('Начало теста')
func(*args)
print('Конец теста')
return call_start_end
prewrapper(test)('Тестируем', 'Ещё не закончили', '...')
# Вывод:
# Начало теста
# Тестируем Ещё не закончили ...
# Конец теста
Так вот буду благодарен, если объясните зачем оборачивать обертку еще одной оберткой, в которую мы отдельно передаем функцию, вместо того, чтобы как в первом примере декоратора, не передавать их в одну обёртку? Мне кажется отчасти декораторы сложны для понимания именно из-за этого момента, сразу так и не ясно зачем дополнительно оборачивать в ещё одну обёртку. Заранее спасибо за ответ.
Ответы (1 шт):
Ответ простой - чтобы использовать эти декораторы не "вручную", а именно как декораторы - задекорировав через @
некоторые функции этим декоратором.
@call_start_end
def test(*args): #Наша функция, тест, которая будет принимать произвольное количество параметров.
print(*args)
test('Тестируем', 'Ещё не закончили', '...')
Ошибка!
TypeError: 'NoneType' object is not callable
@prewrapper
def test(*args):
print(*args)
test('Тестируем', 'Ещё не закончили', '...')
Всё работает как задумано.
Начало теста
Тестируем Ещё не закончили ...
Конец теста
При использовании декораторов через @
вы можете передать в декоратор и аргументы и функцию, но только по отдельности: декоратору - функцию, самой функции - аргументы. Нет единого вызова, чтобы передать сразу всё. А если вы захотите, чтобы ещё и у самого декоратора были какие-то аргументы, которые ему самому можно передать, то вам придётся писать уже целых два враппера:
- внешний враппер будет принимать параметры декоратора
- внутренний будет принимать ссылку на функцию
- функция внутри этих двух врапперов уже будет выполнять собственно нужный вам функционал
Поэтому всё выглядит внешне так сложно, но если разобраться, то всё довольно логично и понятно зачем и почему так всё сделано.
Вообще смысл декораторов в том, что вы никак не меняете основной код, вы по-прежнему вызываете вашу функцию, как будто ничего не изменилось:
test('Тестируем', 'Ещё не закончили', '...')
Вы просто добавили некий модификатор к описанию функции и таким образом меняете её поведение нужным образом, добавляя к нему некий функционал. Что довольно удобно - у вас одна точка изменения, там же, где находится описание самой функции. Вы можете даже применять к функции несколько декораторов по очереди, и это будет выглядеть довольно логично и понятно.