Нелокальное утверждение Python

341

Что делает nonlocalоператор Python (в Python 3.0 и более поздних версиях)?

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

ooboo
источник
4
Взгляните на этот вопрос: stackoverflow.com/questions/1414304/local-functions-in-python
Мэтт Джоунер
18
Вот официальная документация веб-сайта Python для нелокальной структуры: docs.python.org/3/reference/… (эта документация была доступна начиная с Python 3.0, поэтому утверждение ОП об отсутствии официальной документации было просто неверным)
wkschwartz
3
"There is no documentation for nonlocal".На самом деле, вы можете сделать help(keyword_in_string)для документации в Python 3 и выше
ytpillai
10
Честно говоря, официальные документы отчасти сосут на эту тему. Пример выбранного ответа проясняет ситуацию, делая это ценным вопросом.
Безумный физик

Ответы:

473

Сравните это, не используя 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, где inner()'s xтеперь также outer()' s x:

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
скоро
источник
32
Чем это отличается от глобального х?
ooboo
52
Это очень похоже - но обратите внимание, что внешний x не является глобальным в примере, а определен во внешней функции.
Anon
3
@Dustin - На самом деле, если бы у вас был класс A с атрибутом x и определенным в нем подклассом B, вы бы ссылались на x изнутри B как Ax
Anon
2
Код легко получает значительные отступы при определении внутренних функций и в конечном итоге нарушает рекомендацию 79 символов PEP8. Есть ли способ обойти эту проблему? Может ли внутренняя функция как-то быть размещена вне внешней функции? Я знаю, что вопрос звучит глупо, но я серьезно.
tommy.carstensen
3
@ tommy.carstensen, вы можете передать функцию как аргумент, который является красотой функций более высокого порядка. Также в функциональном программировании это называется композицией, python не является чистым языком FP, но вы, безусловно, можете поиграть с функциями (например, генераторы, функции высшего порядка)
superuseroi
90

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

Аркадий
источник
41

Поиск в Google «нелокальный python» обнаружил предложение 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

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

SingleNegationElimination
источник
1
Я был уверен, что это то, что делает ключевое слово «глобальный» - работает в более высоких условиях, пока не достигнет переменной с этим именем. переменная x может быть объявлена ​​на уровне модуля, внутри класса, затем отдельно в функции внутри этого класса и затем во внутренней функции этой функции - как он узнает, на какой x ссылаться?
ooboo
7
Дело в том, что глобальный он работает только для глобальных переменных. он не может видеть переменные во вложенной, неглобальной области видимости.
SingleNegationElimination
Я попробовал make_counter - однако он возвращает не генератор, а функцию. Есть ли способ вернуть генератор, чтобы позже я мог перебрать его?
Dejell
@Dejel: этот пример предназначен для иллюстрации nonlocalоператора в Python; Если вам нужна последовательность натуральных чисел, то идиома Python на самом делеitertools.count()
SingleNegationElimination
Я хотел бы продемонстрировать способность возвращать генератор, как с yield - yield фактически возвращает генератор. Моя идея не в том, чтобы использовать yield, а вместо этого, возможно, использовать нелокальное или другое решение
Dejell
15

@ooboo:

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

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

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

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

Данни Милосавлевич
источник
14

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


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

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

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

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

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

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

Связанные разделы справки: глобальные, NAMESPACES

Источник: Python Language Reference

Йоси Трузман
источник
11
Узнавайте что-то новое каждый день. Я понятия не имел, что вы могли бы использовать help()для ключевых слов (и теперь мой ум взорван: help()без аргументов становится интерактивным ).
Эрик Янгрен
6

Цитата из Python 3 Ссылка :

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

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

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
Jeyekomon
источник
3
a = 0    #1. global variable with respect to every function in program

def f():
    a = 0          #2. nonlocal with respect to function g
    def g():
        nonlocal a
        a=a+1
        print("The value of 'a' using nonlocal is ", a)
    def h():
        global a               #3. using global variable
        a=a+5
        print("The value of a using global is ", a)
    def i():
        a = 0              #4. variable separated from all others
        print("The value of 'a' inside a function is ", a)

    g()
    h()
    i()
print("The value of 'a' global before any function", a)
f()
print("The value of 'a' global after using function f ", a)
gxyd
источник
2

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

Йоси Трузман
источник
0

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

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)
NIPHIN
источник