не могу понять как работает область видимости во вложенных функциях

Рейтинг: 1Ответов: 2Опубликовано: 27.02.2023
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 сразу не видит именованный аргумент, но видит ключ словаря

Ответы

▲ 1Принят

Просто проверим - а какие локальные переменные вообще видны внутри:

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 в заголовке функции. Никаких "секретных" или "скрытых" переменных внутри функции не может оказаться только потому, что вы их описали при вызове функции (а не в описании самой функции).

▲ 0

Параметр **kw это словарь название указания и его значение. Если вы планируете обойтись без переменной создания attempts тогда в second_outer параметры *dargs, **dkwargs надо заменить на attemps (вызов будет либо second_outer(5), либо second_outer(attemps=5)), или на *, attemps (вызов будет только second_outer(attemps=5)). Если я не подробно объяснил прочитайте ответ от @CrazyElf.