Декораторы: почему сохраняется значение локальной переменной
Разбираюсь с декораторами в Python. Пример:
def count(f):
total=0
def decorated(*args, **kwargs):
nonlocal total
total+=1
return f(*args, **kwargs), total
return decorated
@count
def hello(name):
return f"Привет, {name}!"
print(hello("Пользователь_1"))
print(hello("Пользователь_2"))
выводит:
('Привет, Пользователь_1!', 1)
('Привет, Пользователь_2!', 2)
Почему сохраняется значение у переменной total? Если заново запустить весь код, то total опять с 0 начинает отсчет.
Ответы (1 шт):
Не стесняйтесь добавлять в код, который не очень понимаете, отладочную печать:
def count(f):
print('count!') # <-- проверяем запуск функции count
total=0
def decorated(*args, **kwargs):
print('decorated!') # <-- проверяем запуск функции decorated
nonlocal total
total+=1
return f(*args, **kwargs), total
return decorated
Вывод:
count!
decorated!
('Привет, Пользователь_1!', 1)
decorated!
('Привет, Пользователь_2!', 2)
Ну вроде бы всё понятно:
- функция
countбыла вызвана, когда интерпретатору встретился декоратор@count - в этой функции была определена и инициализирована переменная
total = 0 - функция
countвернула ссылку на функциюdecorated - вызовы функции
helloтеперь вызывают функциюdecorated - при этом переменная
totalвнутри функцииdecorated- это как бы переменнаяcount.total, при каждом вызовеhelloиспользуется эта переменная
Обновил ответ.
По сути тут нужно говорить о замыкании. В decorated передалась ссылка на переменную total, инициализированную в функции count. При этом, если вы задекорируете тем же декоратором другую функцию, то ещё раз вызовется функция count и снова инициализирует уже новую переменную total. И эта вторая задекорированная функция будет вести другой счётчик. Из-за чего концепция декораторов такая удобная и получается.
print(hello("Пользователь_1"))
print(hello("Пользователь_2"))
print(hello2("Пользователь_2_1"))
print(hello("Пользователь_3"))
print(hello("Пользователь_4"))
print(hello2("Пользователь_2_2"))
Вывод:
count!
count!
decorated!
('Привет, Пользователь_1!', 1)
decorated!
('Привет, Пользователь_2!', 2)
decorated!
('Привет, Пользователь_2_1!', 1)
decorated!
('Привет, Пользователь_3!', 3)
decorated!
('Привет, Пользователь_4!', 4)
decorated!
('Привет, Пользователь_2_2!', 2)