Просто проверим - а какие локальные переменные вообще видны внутри:
def second_outer(*dargs, **dkwargs):
def outer(func):
def inner(*args, **kwargs):
print(locals())
...
@second_outer(attempts=5)
def div(a, b):
return a / b
print(div(1, 0))
Вывод:
{'args': (1, 0), 'kwargs': {}, 'dkwargs': {'attempts': 5}, 'func': <function div at 0x7fd9a45a3550>}
Ну то есть нет внутри ваших функций никакой отдельной локальной переменной attempts
. Эта "переменная" существует только в виде элемента словаря dkwargs
. И это логично - в аргументах описания декоратора second_outer
такой переменной нет, там есть только словарь dkwargs
, внутри которого она и передаётся. И это правильно - мало ли что вы там снаружи захотите передать, вдруг вы захотите перекрыть какую-то уже существующую локальную переменную, например. Небезопасно это - создавать произвольные переменные просто потому, что функция вызвана с таким аргументом. А вот имя словаря жёстко прописано в заголовке функции, никаких проблем и неожиданностей это точно не вызовет.
Вывод: внутри функций существуют только те локальные переменные (да и глобальные тоже), которые были в явном виде где-то описаны. Параметры функции, задаваемые при вызове функции, передаются либо в виде явно описанных в описании функции аргументов, либо в виде словаря в виде описания **kwargs
в заголовке функции. Никаких "секретных" или "скрытых" переменных внутри функции не может оказаться только потому, что вы их описали при вызове функции (а не в описании самой функции).