Нелокален израз на Python

Какво прави изразът nonlocal на Python (в Python 3.0 и по-нови)?

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


person ooboo    schedule 11.08.2009    source източник
comment
Разгледайте този въпрос: stackoverflow.com/questions/1414304/local -functions-in-python   -  person Matt Joiner    schedule 05.12.2010
comment
Ето официалната документация на уебсайта на Python за nonlocal: 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
така че ви позволява да правите вътрешни класове, които препращат към външни променливи, като 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 всъщност е itertools.count() - 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


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

Изразът nonlocal кара изброените идентификатори да препращат към предварително обвързани променливи в най-близкия обхващащ обхват. Това е важно, тъй като поведението по подразбиране за обвързване е първо да се търси локалното пространство от имена. Изявлението позволява на капсулиран код да свързва отново променливи извън локалния обхват освен глобалния (модулен) обхват.

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

Имената, изброени в оператор nonlocal, не трябва да се сблъскват с вече съществуващи обвързвания в локалния обхват.

Вижте също:

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

Свързани теми за помощ: глобално, NAMESPACES

Източник: Справочник за езика Python

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

Цитат от Справочник за 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
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