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


341

Що робить nonlocalоператор Python (у Python 3.0 та пізніших версіях)?

На офіційному веб-сайті Python жодна документація help("nonlocal")також не працює.


4
Погляньте на це запитання: stackoverflow.com/questions/1414304/local-functions-in-python
Matt Joiner

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 як Axe
Anon

2
Код легко визначається сильним відступом при визначенні внутрішніх функцій і в кінцевому підсумку порушує рекомендацію PEP8 на 79 символів. Будь-який спосіб подолати цю проблему? Чи може внутрішня функція якось розміщуватися поза зовнішньою функцією? Я знаю, що питання звучить нерозумно, але я серйозно.
tommy.carstensen

3
@ tommy.carstensen ви можете передати функцію як аргумент, який є красою функцій вищого порядку. Також у функціональному програмуванні це називається композицією, python не є чистою мовою FP, але ви, звичайно, можете грати з функціями (генератори, функції вищого порядку - деякі приклади)
superuseroi

90

Коротше кажучи, він дозволяє призначити значення змінній у зовнішній (але не глобальній) області. Дивіться PEP 3104 про всі деталі горі.


41

Пошуковий пошук в 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

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


1
Я був впевнений, що саме це робить ключове слово "global" - працює над вищими поведінками, поки не досягне змінної з цим ім'ям. змінна x може бути оголошена на рівні модуля, всередині класу, потім окремо у функції всередині цього класу, а потім у внутрішній функції цієї функції - як дізнатися, до якого x слід звернутися?
ooboo

7
річ у глобальному полягає в тому, що він працює лише для глобальних змінних. він не може бачити змінні в неглобальній області, що прикладається.
SingleNegationElimination

Я спробував make_counter - однак він не повертає генератор, а функцію. Чи є спосіб повернути генератор, щоб пізніше я міг повторити його?
Dejell

@Dejel: цей приклад призначений для ілюстрації nonlocalтвердження в Python; Якщо ви хочете послідовність натуральних чисел, ідіома пітона є насправдіitertools.count()
SingleNegationElimination

Я хотів би продемонструвати можливість повернення генератора, як з урожайністю - вихід фактично повертає генератор. Моя ідея - не використовувати врожай, а замість цього, можливо, використовувати нелокальне чи інше рішення
Dejell

15

@ooboo:

Він бере одну "найближчу" до точки відліку у вихідному коді. Це називається "Лексична оцінка" і є стандартною для> 40 років.

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

Якщо ви не вкажете, nonlocalале зробите x = 7, це створить нову локальну змінну "x". Якщо ви вкажете nonlocal, він знайде "найближчий" "х" і призначить цьому. Якщо ви вкажете nonlocalі немає "х", воно видасть вам повідомлення про помилку.

Ключове слово globalзавжди здавалося мені дивним, оскільки воно з радістю ігнорує всі інші "х", крім самого зовнішнього. Дивно.


14

help ('nonlocal') nonlocalЗаява


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

The nonlocalЗатвердження викликає перераховані ідентифікатори для позначення раніше пов'язаних змінних в найближчій області видимості. Це важливо, оскільки поведінка за замовчуванням для прив'язки - це спочатку пошук локального простору імен. Заява дозволяє інкапсульованому коду відновлювати змінні за межами локальної області, крім глобальної (модульної) області.

Імена, перелічені у nonlocalвиписці, на відміну від тих, що вказані в global виписці, повинні посилатися на попередні прив'язки в обкладинці (область, у якій має бути створена нова прив'язка, не може бути визначена однозначно).

Імена, перелічені у nonlocalвиписці, не повинні стикатися з наявними прив'язками в локальному масштабі.

Дивись також:

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

Пов’язані теми довідки: глобальний, ІМЕНИ

Джерело: 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

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)

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.