Область видимости у декоратора

Есть такой код:

def logger(f):
    def inner(*args, **kwargs):
        a = 6
        return f(*args, **kwargs)
    return inner
@logger
def nothing():
    print(a)

Выходит ошибка, что переменная a неопределенна, не совсем понимаю по какому принципу он ищет ее в области видимости. Можете объяснить?


Ответы (1 шт):

Автор решения: alex

у "декоратора" такая же область видимости что и у любого другого метода. в данном случае переменная a объявлена в методе inner и видна только в нем.

если вынести объявление переменной a из inner в logger, то a будет видна и в logger и в inner. поэтому слово декоратор я взял в ковычки, потому что декоратор это паттерн, а вопрос об области видимости вложенных методов. однако в методе nothing переменная a не будет доступна никогда, потому что он выполняется в своей области видимости.

def logger(f):
    a = ""
    print("logger:", locals())

    def inner(*args, **kwargs):
        nonlocal a
        b = ""
        print("logger.inner:", locals())
        return f(*args, **kwargs)
    return inner


@logger
def nothing():
    c = ""
    print("nothing:", locals())

nothing()
logger: {'a': '', 'f': <function nothing at 0x7f981115f4d0>}
logger.inner: {'args': (), 'kwargs': {}, 'b': '', 'a': '', 'f': <function nothing at 0x7f981115f4d0>}
nothing: {'c': ''}

для вложенной функции inner переменные объявленные в функции logger доступны в режиме read-only. ключевое слово nonlocal в данном случае позволяет изменять переменную объявленнию на скоуп выше.

интересно, что без nonlocal переменная a не видна в locals() если она не упоминается в коде inner.

def logger(f):
    a = ""
    print("logger:", locals())

    def inner(*args, **kwargs):
        b = ""
        print("logger.inner:", locals())
        print(a)
        return f(*args, **kwargs)
    return inner


@logger
def nothing():
    c = ""
    print("nothing:", locals())

nothing()
logger: {'a': '', 'f': <function nothing at 0x7faa790214d0>}
logger.inner: {'args': (), 'kwargs': {}, 'b': '', 'a': '', 'f': <function nothing at 0x7faa790214d0>}

nothing: {'c': ''}
def logger(f):
    a = ""
    print("logger:", locals())

    def inner(*args, **kwargs):
        b = ""
        print("logger.inner:", locals())
        return f(*args, **kwargs)
    return inner


@logger
def nothing():
    c = ""
    print("nothing:", locals())

nothing()
logger: {'a': '', 'f': <function nothing at 0x7f20117c44d0>}
logger.inner: {'args': (), 'kwargs': {}, 'b': '', 'f': <function nothing at 0x7f20117c44d0>}
nothing: {'c': ''}

а вот метод nothing никакого отношения к logger и inner не имеет.

→ Ссылка