Декораторы: почему сохраняется значение локальной переменной
Разбираюсь с декораторами в 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)