Нелокальный оператор Python

Что делает оператор Python nonlocal (в Python 3.0 и новее)?

На официальном сайте Python нет документации, help("nonlocal") тоже не работает.


person ooboo    schedule 11.08.2009    source источник
comment
Взгляните на этот вопрос: stackoverflow.com/questions/1414304/local -функции-в-питоне   -  person Matt Joiner    schedule 05.12.2010
comment
Вот официальная документация веб-сайта Python для нелокальных пользователей: docs.python. org / 3 / reference / (эта документация доступна начиная с Python 3.0, поэтому утверждение OP об отсутствии официальной документации просто неверно)   -  person wkschwartz    schedule 03.10.2013
comment
"There is no documentation for nonlocal". На самом деле, вы можете делать help(keyword_in_string) для документации на Python 3 и выше.   -  person ytpillai    schedule 07.08.2015
comment
Честно говоря, официальные документы отстой в этой теме. Пример выбранного ответа проясняет ситуацию, что делает этот вопрос ценным.   -  person Mad Physicist    schedule 27.05.2016
comment
В официальном руководстве по Python есть хорошее объяснение концепция областей и пространств имен с помощью приятного пример.   -  person jammon    schedule 08.10.2016
comment
Вот официальная документация с примером, и все проясняет.   -  person Ketan    schedule 29.01.2020
comment
@MadPhysicist Как вы думаете, почему документы - отстой? Искренне любопытно. Я нашел help('nonlocal') довольно ясным.   -  person EntangledLoops    schedule 23.07.2020
comment
@EntangledLoops. Я подозреваю, что это было не так более 5 лет назад, когда я делал этот комментарий?   -  person Mad Physicist    schedule 23.07.2020
comment
@MadPhysicist Я тоже мог так подумать, но есть ответ примерно 9 лет назад ниже, который скопировал / вставил текст, и он выглядит так же, как сегодня.   -  person EntangledLoops    schedule 24.07.2020
comment
@EntangledLoops. Кроме того, я мало что знал пять лет назад   -  person Mad Physicist    schedule 24.07.2020


Ответы (8)


Сравните это без использования nonlocal:

x = 0
def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 0

Для этого, используя nonlocal, где x inner() теперь также x outer():

x = 0
def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 2
# global: 0

Если бы мы использовали global, он бы привязал x к правильному «глобальному» значению:

x = 0
def outer():
    x = 1
    def inner():
        global x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 2
person Anon    schedule 11.08.2009
comment
Это очень похоже, но обратите внимание, что внешний x не является глобальным в примере, а вместо этого определяется во внешней функции. - person Anon; 11.08.2009
comment
поэтому он позволяет вам создавать внутренние классы, которые ссылаются на внешние vars, например java? - person Dustin Getz; 11.08.2009
comment
@Dustin - На самом деле, если бы у вас был класс A с атрибутом x и определенным в нем подклассом B, вы бы ссылались на x изнутри B как на A.x - person Anon; 11.08.2009
comment
Да, внутренние классы Java имеют аналогичное поведение. - person apt1002; 12.12.2013
comment
Код легко получает большие отступы при определении внутренних функций и в конечном итоге нарушает рекомендацию PEP8, состоящую из 79 символов. Есть ли способ обойти эту проблему? Можно ли каким-то образом поместить внутреннюю функцию за пределы внешней функции? Я знаю, что вопрос звучит глупо, но я серьезно. - person tommy.carstensen; 12.02.2015
comment
@ tommy.carstensen вы можете передать функцию как аргумент, в этом прелесть функций высшего порядка. Также в функциональном программировании это называется композицией, python не является чистым языком FP, но вы, безусловно, можете поиграть с функциями (генераторы, функции более высокого порядка - некоторые примеры) - person superuseroi; 31.03.2015
comment
так что, по сути, практическое правило - использовать nonlocal, когда внутренняя функция пытается изменить одно из своих замыканий? - person pkaramol; 16.07.2019
comment
Что делать, если есть 3 вложенные функции? Тогда есть ли способ получить доступ ко всем 4 уровням из самой внутренней функции? - person Rastapopoulos; 09.10.2020
comment
Кажется, объекты, такие как массивы и словари, работают по-другому. По умолчанию они нелокальны. Это верно? Что, если я хочу, чтобы они были местными? - person Hans; 21.07.2021

Короче говоря, он позволяет вам присваивать значения переменной во внешней (но неглобальной) области. См. Подробности в PEP 3104.

person Arkady    schedule 11.08.2009

Поиск в Google по запросу "python nonlocal" обнаружил предложение, PEP 3104, который полностью описывает синтаксис и аргументы, лежащие в основе утверждения. Короче говоря, он работает точно так же, как оператор global, за исключением того, что он используется для ссылки на переменные, которые не являются ни глобальными, ни локальными для функции.

Вот краткий пример того, что вы можете с этим сделать. Генератор счетчиков можно переписать, чтобы использовать это так, чтобы он больше походил на идиомы языков с замыканиями.

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

Очевидно, вы можете написать это как генератор, например:

def counter_generator():
    count = 0
    while True:
        count += 1
        yield count

Но, хотя это совершенно идиоматический питон, похоже, что первая версия будет немного более очевидной для новичков. Правильное использование генераторов путем вызова возвращаемой функции - обычная путаница. Первая версия явно возвращает функцию.

person SingleNegationElimination    schedule 11.08.2009
comment
Я был уверен, что именно это и делает ключевое слово global - работает в более высоких средах, пока не достигнет переменной с таким именем. переменная x может быть объявлена ​​на уровне модуля, внутри класса, затем отдельно в функции внутри этого класса, а затем во внутренней функции этой функции - как она узнает, на какой x ссылаться? - person ooboo; 11.08.2009
comment
главное в global то, что он работает только для глобальных переменных. он не может видеть переменные во внешней неглобальной области видимости. - person SingleNegationElimination; 11.10.2009
comment
Я пробовал make_counter, но он возвращает не генератор, а функцию. есть ли способ вернуть генератор, чтобы позже я мог перебрать его? - person Dejell; 05.12.2013
comment
@Dejel: этот пример предназначен для иллюстрации оператора nonlocal в Python; Если вам нужна последовательность натуральных чисел, идиома python на самом деле _2 _ - person SingleNegationElimination; 05.12.2013
comment
Я хотел бы продемонстрировать возможность возврата генератора, как с yield - yield фактически возвращает генератор. Моя идея - не использовать yield, а вместо этого, возможно, использовать нелокальное или другое решение - person Dejell; 05.12.2013
comment
@Dejel: Похоже, у вас проблема, которая немного отличается от той, о которой спрашивали ooboo. Пожалуйста, задайте новый вопрос вместо того, чтобы использовать здесь раздел комментариев. - person SingleNegationElimination; 05.12.2013

@ooboo:

Берется «самый близкий» к исходной точке ссылки. Это называется «лексическая область видимости» и является стандартом уже более 40 лет.

Члены класса Python действительно находятся в словаре под названием __dict__ и никогда не будут доступны с помощью лексической области видимости.

Если вы не укажете nonlocal, но укажете x = 7, будет создана новая локальная переменная «x». Если вы укажете nonlocal, он найдет «ближайший» «x» и назначит ему. Если вы укажете nonlocal и не укажете «x», появится сообщение об ошибке.

Ключевое слово global всегда казалось мне странным, поскольку оно с радостью игнорирует все остальные «x», кроме самого внешнего. Странный.

person Danny Milosavljevic    schedule 05.12.2010

help ('нелокальный') Оператор nonlocal


    nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*

Оператор nonlocal заставляет перечисленные идентификаторы ссылаться на ранее связанные переменные в ближайшей охватывающей области. Это важно, поскольку по умолчанию привязка выполняется сначала в локальном пространстве имен. Оператор позволяет инкапсулированному коду повторно связывать переменные вне локальной области, помимо глобальной (модульной) области.

Имена, перечисленные в операторе nonlocal, в отличие от имен, перечисленных в операторе global, должны относиться к ранее существовавшим привязкам во включающей области (область, в которой должна быть создана новая привязка, не может быть определена однозначно).

Имена, перечисленные в операторе nonlocal, не должны конфликтовать с ранее существовавшими привязками в локальной области.

Смотрите также:

PEP 3104 - доступ к именам во внешних областях - спецификация для оператора nonlocal.

Связанные разделы справки: global, NAMESPACES

Источник: Справочник по языку Python

person Yossi Truzman    schedule 04.08.2011
comment
Узнавайте что-то новое каждый день. Я понятия не имел, что вы можете использовать help() в ключевых словах (и теперь я просто потрясен: help() без аргументов становится интерактивным). - person Erik Youngren; 24.02.2014

Цитата из Справочника по Python 3:

Оператор nonlocal заставляет перечисленные идентификаторы ссылаться на ранее связанные переменные в ближайшей охватывающей области, за исключением глобальных переменных.

Как сказано в справке, в случае нескольких вложенных функций изменяется только переменная в ближайшей включающей функции:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        x = 2
        innermost()
        if x == 3: print('Inner x has been modified')

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Inner x has been modified

«Ближайшая» переменная может находиться на расстоянии нескольких уровней:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Outer x has been modified

Но это не может быть глобальная переменная:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    inner()

x = 0
outer()
if x == 3: print('Global x has been modified')

# SyntaxError: no binding for nonlocal 'x' found
person Jeyekomon    schedule 21.03.2018

Мое личное понимание «нелокального» оператора (и прошу прощения, так как я новичок в Python и программировании в целом) заключается в том, что «нелокальный» - это способ использовать глобальные функции в повторяющихся функциях, а не тело самого кода. . Глобальный оператор между функциями, если хотите.

person Yossi Truzman    schedule 04.08.2011

с «нелокальными» внутренними функциями (т.е. вложенными внутренними функциями) может получить разрешение на чтение & 'запись' для этой конкретной переменной внешней родительской функции . А нелокальный можно использовать только внутри внутренних функций, например:

a = 10
def Outer(msg):
    a = 20
    b = 30
    def Inner():
        c = 50
        d = 60
        print("MU LCL =",locals())
        nonlocal a
        a = 100
        ans = a+c
        print("Hello from Inner",ans)       
        print("value of a Inner : ",a)
    Inner()
    print("value of a Outer : ",a)

res = Outer("Hello World")
print(res)
print("value of a Global : ",a)
person NIPHIN    schedule 21.03.2018