не могу понять как работает область видимости во вложенных функциях
def second_outer(*dargs, **dkwargs):
def outer(func):
def inner(*args, **kwargs):
attempts = dkwargs['attempts']
while attempts > 0: # ???
try:
return func(*args, **kwargs)
except Exception as error:
print('Error', error)
attempts -= 1
return inner
return outer
@second_outer(attempts=5)
def div(a, b):
return a / b
print(div(1, 0))
Не понимаю, почему while сразу не видит именованный аргумент, но видит ключ словаря
Ответы (2 шт):
Просто проверим - а какие локальные переменные вообще видны внутри:
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 в заголовке функции. Никаких "секретных" или "скрытых" переменных внутри функции не может оказаться только потому, что вы их описали при вызове функции (а не в описании самой функции).
Параметр **kw это словарь название указания и его значение. Если вы планируете обойтись без переменной создания attempts тогда в second_outer параметры *dargs, **dkwargs надо заменить на attemps (вызов будет либо second_outer(5), либо second_outer(attemps=5)), или на *, attemps (вызов будет только second_outer(attemps=5)). Если я не подробно объяснил прочитайте ответ от @CrazyElf.