Breve descrizione delle regole di scoping?


472

Quali sono esattamente le regole di scoping di Python?

Se ho del codice:

code1
class Foo:
   code2
   def spam.....
      code3
      for code4..:
       code5
       x()

Dove si xtrova? Alcune possibili scelte includono l'elenco seguente:

  1. Nel file sorgente allegato
  2. Nel namespace della classe
  3. Nella definizione della funzione
  4. Nella variabile indice del ciclo for
  5. All'interno del ciclo for

Inoltre c'è il contesto durante l'esecuzione, quando la funzione spamviene passata da qualche altra parte. E forse le funzioni lambda passano in modo leggermente diverso?

Ci deve essere un semplice riferimento o un algoritmo da qualche parte. È un mondo confuso per i programmatori Python intermedi.


2
Le regole di scoping sono descritte abbastanza tersamente - ma anche completamente - nella documentazione di Python: docs.python.org/3/reference/… .
jefe2000,

Risposte:


420

In realtà, una regola concisa per la risoluzione di Python Scope, da Learning Python, 3a. Ed. . (Queste regole sono specifiche per i nomi delle variabili, non per gli attributi. Se si fa riferimento senza un punto, si applicano queste regole.)

Regola delle gambe

  • L ocale: nomi assegnati in qualsiasi modo all'interno di una funzione ( defo lambda) e non dichiarati globali in quella funzione

  • E nclosing-function - Nomi assegnati nell'ambito locale di tutte le funzioni ( defo lambda) che racchiudono staticamente , da interno a esterno

  • sol lobal (modulo) - Nomi assegnati al livello superiore di un file del modulo o eseguendo globalun'istruzione in a defall'interno del file

  • B uilt-in (Python) - Nomi preassegnati nel modulo nomi incorporato:open , range, SyntaxError, ecc

Quindi, nel caso di

code1
class Foo:
    code2
    def spam():
        code3
        for code4:
            code5
            x()

Il for loop non ha un proprio spazio dei nomi. In ordine LEGB, gli scopi sarebbero

  • L: Local in def spam(incode3 , code4e code5)
  • E: Qualsiasi funzione che racchiude (se l'intero esempio fosse in un altro def)
  • G: Ce n'erano x dichiarati a livello globale nel modulo (in code1)?
  • B: Qualsiasi incorporato x in in Python.

xnon verrà mai trovato in code2(anche nei casi in cui ci si potrebbe aspettare, vedere la risposta di Antti o qui ).


45
Come avvertimento per l'accesso globale: la lettura di una variabile globale può avvenire senza una dichiarazione esplicita, ma scriverla senza dichiarare globale (nome_var) creerà invece una nuova istanza locale.
Peter Gibson,

12
In realtà @Peter, global(var_name)è sintatticamente errato. La sintassi corretta sarebbe global var_namesenza parentesi. Hai un punto valido però.
martineau,

Se è così, allora perché la variabile "y" di foo non è visibile alla "barra" in basso: >>> def foo(x): ... y = x ... def bar(z): ... y = z ... bar(5) ... print x,y ... >>> foo(3) 3 3
Jonathan Mayer,

3
@Jonathan: Perché ognuno yè stato scritto e non ci sono global ydichiarazioni - vedi il commento di @ Peter.
martineau,

@LakshmanPrasad Cade in "E", ma ha un comportamento speciale che vale la pena menzionare: è una variabile di classe, quindi è un "globale" tra i suoi oggetti. Assegnare ad esso sarà portare a imprevedibili e difficili da eseguire il debug di problemi se non si sa cosa si sta facendo.
Ctrl-C

157

In sostanza, l'unica cosa in Python che introduce un nuovo ambito è una definizione di funzione. Le classi sono un po 'un caso speciale in quanto tutto ciò che è definito direttamente nel corpo viene inserito nello spazio dei nomi della classe, ma non sono direttamente accessibili dall'interno dei metodi (o classi nidificate) che contengono.

Nel tuo esempio ci sono solo 3 ambiti in cui verrà cercato x:

  • ambito dello spam - contenente tutto ciò che è definito in code3 e code5 (oltre a code4, la variabile del tuo loop)

  • L'ambito globale - contenente tutto ciò che è definito in code1, così come Foo (e qualunque cosa cambi dopo)

  • Lo spazio dei nomi incorporato. Un po 'un caso speciale: contiene le varie funzioni e tipi predefiniti di Python come len () e str (). Generalmente questo non dovrebbe essere modificato da nessun codice utente, quindi aspettati che contenga le funzioni standard e nient'altro.

Più ambiti vengono visualizzati solo quando si introduce una funzione nidificata (o lambda) nell'immagine. Questi si comporteranno praticamente come ti aspetteresti comunque. La funzione nidificata può accedere a tutto nell'ambito locale, nonché a qualsiasi cosa nell'ambito della funzione che lo racchiude. per esempio.

def foo():
    x=4
    def bar():
        print x  # Accesses x from foo's scope
    bar()  # Prints 4
    x=5
    bar()  # Prints 5

restrizioni:

È possibile accedere a variabili in ambiti diversi dalle variabili della funzione locale, ma non è possibile rimbalzare verso nuovi parametri senza ulteriore sintassi. Invece, l'assegnazione creerà una nuova variabile locale invece di influire sulla variabile nell'ambito genitore. Per esempio:

global_var1 = []
global_var2 = 1

def func():
    # This is OK: It's just accessing, not rebinding
    global_var1.append(4) 

    # This won't affect global_var2. Instead it creates a new variable
    global_var2 = 2 

    local1 = 4
    def embedded_func():
        # Again, this doen't affect func's local1 variable.  It creates a 
        # new local variable also called local1 instead.
        local1 = 5
        print local1

    embedded_func() # Prints 5
    print local1    # Prints 4

Per modificare effettivamente i binding delle variabili globali all'interno di un ambito di funzione, è necessario specificare che la variabile è globale con la parola chiave globale. Per esempio:

global_var = 4
def change_global():
    global global_var
    global_var = global_var + 1

Attualmente non c'è modo di fare lo stesso per le variabili che racchiudono gli ambiti delle funzioni , ma Python 3 introduce una nuova parola chiave " nonlocal", che agirà in modo simile a quelli globali, ma per gli ambiti di funzioni nidificati.


111

Non c'è stata una risposta completa riguardo al tempo di Python3, quindi ho fatto una risposta qui. Gran parte di ciò che è descritto qui è dettagliato nella Risoluzione 4.2.2 dei nomi della documentazione di Python 3.

Come indicato in altre risposte, ci sono 4 ambiti di base, il LEGB, per Local, Enclosing, Global e Builtin. Oltre a questi, esiste un ambito speciale, il corpo della classe , che non comprende un ambito di applicazione per i metodi definiti all'interno della classe; qualsiasi assegnazione all'interno del corpo della classe rende la variabile da lì in poi vincolata nel corpo della classe.

Soprattutto, nessuna istruzione di blocco, a parte defeclass , creare un ambito variabile. In Python 2 la comprensione di una lista non crea un ambito variabile, tuttavia in Python 3 la variabile di ciclo all'interno della comprensione di un elenco viene creata in un nuovo ambito.

Per dimostrare le peculiarità del corpo di classe

x = 0
class X(object):
    y = x
    x = x + 1 # x is now a variable
    z = x

    def method(self):
        print(self.x) # -> 1
        print(x)      # -> 0, the global x
        print(y)      # -> NameError: global name 'y' is not defined

inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)

Pertanto, diversamente dal corpo della funzione, è possibile riassegnare la variabile allo stesso nome nel corpo della classe, per ottenere una variabile di classe con lo stesso nome; ulteriori ricerche su questo nome vengono invece risolte nella variabile di classe.


Una delle maggiori sorprese per molti nuovi arrivati ​​su Python è che un forloop non crea un ambito variabile. In Python 2 la comprensione dell'elenco non crea neanche un ambito (mentre lo fanno i generatori e le comprensioni dict!) Invece perdono il valore nella funzione o nell'ambito globale:

>>> [ i for i in range(5) ]
>>> i
4

Le comprensioni possono essere usate come un modo astuto (o terribile se vuoi) per creare variabili modificabili all'interno delle espressioni lambda in Python 2 - un'espressione lambda crea un ambito variabile, come il def farebbe affermazione, ma all'interno di lambda non sono consentite istruzioni. L'assegnazione essendo un'istruzione in Python significa che non sono consentite assegnazioni variabili in lambda, ma la comprensione di un elenco è un'espressione ...

Questo comportamento è stato corretto in Python 3: nessuna espressione di comprensione o variabili di perdita dei generatori.


Il globale significa davvero l'ambito del modulo; il modulo principale di Python è il __main__; tutti i moduli importati sono accessibili tramite la sys.modulesvariabile; per ottenere l'accesso __main__si può usare sys.modules['__main__'], oppure import __main__; è perfettamente accettabile accedere e assegnare attributi lì; verranno visualizzati come variabili nell'ambito globale del modulo principale.


Se viene mai assegnato un nome nell'ambito corrente (tranne nell'ambito della classe), verrà considerato appartenente a tale ambito, altrimenti verrà considerato come appartenente a qualsiasi ambito racchiuso che assegna alla variabile (potrebbe non essere assegnato ancora, o per niente), o infine l'ambito globale. Se la variabile è considerata locale, ma non è stata ancora impostata o è stata eliminata, si otterrà la lettura del valore della variabile UnboundLocalError, che è una sottoclasse di NameError.

x = 5
def foobar():
    print(x)  # causes UnboundLocalError!
    x += 1    # because assignment here makes x a local variable within the function

# call the function
foobar()

L'ambito può dichiarare che desidera esplicitamente modificare la variabile globale (ambito del modulo), con la parola chiave globale:

x = 5
def foobar():
    global x
    print(x)
    x += 1

foobar() # -> 5
print(x) # -> 6

Anche questo è possibile anche se è stato ombreggiato nell'ambito compreso:

x = 5
y = 13
def make_closure():
    x = 42
    y = 911
    def func():
        global x # sees the global value
        print(x, y)
        x += 1

    return func

func = make_closure()
func()      # -> 5 911
print(x, y) # -> 6 13

In python 2 non esiste un modo semplice per modificare il valore nell'ambito compreso; di solito questo viene simulato con un valore modificabile, come un elenco con lunghezza 1:

def make_closure():
    value = [0]
    def get_next_value():
        value[0] += 1
        return value[0]

    return get_next_value

get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2

Tuttavia in Python 3, nonlocalviene in soccorso:

def make_closure():
    value = 0
    def get_next_value():
        nonlocal value
        value += 1
        return value
    return get_next_value

get_next = make_closure() # identical behavior to the previous example.

La nonlocaldocumentazione dice che

I nomi elencati in un'istruzione non locale, a differenza di quelli elencati in un'istruzione globale, devono fare riferimento a associazioni preesistenti in un ambito di chiusura (l'ambito in cui creare una nuova associazione non può essere determinato in modo univoco).

ie nonlocalsi riferisce sempre all'ambito non globale esterno più interno a cui è stato associato il nome (ovvero assegnato a, incluso usato come forvariabile target, nella withclausola o come parametro di funzione).


Qualsiasi variabile che non è considerata locale per l'ambito corrente, o qualsiasi ambito incluso, è una variabile globale. Un nome globale viene cercato nel dizionario globale del modulo; se non trovato, il globale viene quindi cercato dal modulo incorporato; il nome del modulo è stato cambiato da python 2 a python 3; in Python 2 lo era __builtin__e in Python 3 è ora chiamato builtins. Se si assegna a un attributo del modulo incorporato, sarà successivamente visibile a qualsiasi modulo come variabile globale leggibile, a meno che quel modulo non li ombreggi con la propria variabile globale con lo stesso nome.


La lettura del modulo integrato può anche essere utile; supponiamo che tu voglia la funzione di stampa in stile python 3 in alcune parti del file, ma altre parti del file usano ancora l' printistruzione. In Python 2.6-2.7 puoi ottenere la printfunzione Python 3 con:

import __builtin__

print3 = __builtin__.__dict__['print']

In from __future__ import print_functionrealtà, la printfunzione non importa da nessuna parte in Python 2, ma disabilita semplicemente le regole di analisi per l' printistruzione nel modulo corrente, gestendola printcome qualsiasi altro identificatore di variabile e consentendo così la ricerca printdella funzione nei builtin.


23

Le regole di scoping per Python 2.x sono già state delineate in altre risposte. L'unica cosa che aggiungerei è che in Python 3.0 esiste anche il concetto di ambito non locale (indicato dalla parola chiave "non locale"). Ciò ti consente di accedere direttamente agli ambiti esterni e ti dà la possibilità di fare alcuni trucchi accurati, comprese le chiusure lessicali (senza brutti hack che coinvolgono oggetti mutabili).

EDIT: Ecco il PEP con maggiori informazioni su questo.


23

Un esempio leggermente più completo di ambito:

from __future__ import print_function  # for python 2 support

x = 100
print("1. Global x:", x)
class Test(object):
    y = x
    print("2. Enclosed y:", y)
    x = x + 1
    print("3. Enclosed x:", x)

    def method(self):
        print("4. Enclosed self.x", self.x)
        print("5. Global x", x)
        try:
            print(y)
        except NameError as e:
            print("6.", e)

    def method_local_ref(self):
        try:
            print(x)
        except UnboundLocalError as e:
            print("7.", e)
        x = 200 # causing 7 because has same name
        print("8. Local x", x)

inst = Test()
inst.method()
inst.method_local_ref()

produzione:

1. Global x: 100
2. Enclosed y: 100
3. Enclosed x: 101
4. Enclosed self.x 101
5. Global x 100
6. global name 'y' is not defined
7. local variable 'x' referenced before assignment
8. Local x 200

6
Questa è un'ottima risposta Tuttavia, penso che le differenze tra methode method_local_refdebbano essere evidenziate. methodè in grado di accedere alla variabile globale e stamparla come in 5. Global x. Ma method_local_refnon è possibile perché in seguito definisce una variabile locale con lo stesso nome. Puoi testarlo rimuovendo la x = 200linea e vedere la differenza
Kiril

@brianray: che dire di z?
Malik A. Rumi,

@kiril Ho aggiunto una nota a riguardo
brianray il

@ MalikA.Rumi Ho rimosso z perché non era interessante
brianray il

Sorprendentemente, questa è l' unica chiara spiegazione degli ambiti Python, che ho potuto trovare su tutto il SO. Semplicemente usando un esempio molto semplice. Grazie!
not2qubit

13

Python risolve le tue variabili con - generalmente - tre spazi dei nomi disponibili.

In qualsiasi momento durante l'esecuzione, ci sono almeno tre ambiti nidificati i cui spazi dei nomi sono direttamente accessibili: l'ambito più interno, che viene cercato per primo, contiene i nomi locali; gli spazi dei nomi di tutte le funzioni che si racchiudono e che vengono cercati a partire dall'ambito di chiusura più vicino; l'ambito centrale, cercato successivamente, contiene i nomi globali del modulo corrente; e l'ambito più esterno (cercato per ultimo) è lo spazio dei nomi che contiene i nomi incorporati.

Esistono due funzioni: globalse localsche mostrano i contenuti di due di questi spazi dei nomi.

Gli spazi dei nomi sono creati da pacchetti, moduli, classi, costruzione di oggetti e funzioni. Non ci sono altri gusti di spazi dei nomi.

In questo caso, la chiamata a una funzione denominata xdeve essere risolta nello spazio dei nomi locale o nello spazio dei nomi globale.

Locale in questo caso, è il corpo della funzione metodo Foo.spam.

Globale è - beh - globale.

La regola è cercare gli spazi locali nidificati creati dalle funzioni del metodo (e le definizioni delle funzioni nidificate), quindi effettuare una ricerca globale. Questo è tutto.

Non ci sono altri ambiti. L' foristruzione (e altre istruzioni composte come ife try) non creano nuovi ambiti nidificati. Solo definizioni (pacchetti, moduli, funzioni, classi ed istanze di oggetti).

All'interno di una definizione di classe, i nomi fanno parte dello spazio dei nomi della classe. code2, ad esempio, deve essere qualificato dal nome della classe. In generale Foo.code2. Tuttavia, self.code2funzionerà anche perché gli oggetti Python considerano la classe contenitore come un fallback.

Un oggetto (un'istanza di una classe) ha variabili di istanza. Questi nomi si trovano nello spazio dei nomi dell'oggetto. Devono essere qualificati dall'oggetto. ( variable.instance.)

All'interno di un metodo di classe, hai gente del posto e globali. Dici self.variabledi scegliere l'istanza come spazio dei nomi. Noterai che selfè un argomento per ogni funzione del membro della classe, rendendolo parte dello spazio dei nomi locale.

Vedi Regole di ambito Python , Ambito Python , Ambito variabile .


5
Questo è scaduto. Dal 2.1 (7 anni fa) ci sono più di due ambiti, poiché le funzioni nidificate introducono nuovi ambiti, quindi una funzione all'interno di una funzione avrà accesso al suo ambito locale, all'ambito delle funzioni che racchiude e all'ambito globale (anche builtin).
Brian,

Mi dispiace, non è più così. Python has two namespaces available. Global and local-to-something.
Rizwan Kassim,

9

Dove si trova x?

x non viene trovato poiché non è stato definito. :-) Potrebbe essere trovato in code1 (globale) o code3 (locale) se lo metti lì.

code2 (membri della classe) non sono visibili al codice all'interno dei metodi della stessa classe - di solito si accede ad essi usando self. code4 / code5 (loop) vivono nello stesso ambito di code3, quindi se scrivessi su x lì cambierai l'istanza x definita in code3, non facendo una nuova x.

Python ha un ambito statico, quindi se si passa "spam" a un'altra funzione, lo spam avrà comunque accesso ai globi nel modulo da cui proviene (definito nel codice 1) e qualsiasi altro ambito contenente (vedi sotto). I membri di code2 sarebbero di nuovo accessibili da soli.

lambda non è diverso da def. Se si utilizza un lambda all'interno di una funzione, è lo stesso che definire una funzione nidificata. A partire da Python 2.2 sono disponibili ambiti nidificati. In questo caso puoi associare x a qualsiasi livello di annidamento delle funzioni e Python prenderà l'istanza più interna:

x= 0
def fun1():
    x= 1
    def fun2():
        x= 2
        def fun3():
            return x
        return fun3()
    return fun2()
print fun1(), x

2 0

fun3 vede l'istanza x dall'ambito contenitore più vicino, che è l'ambito della funzione associato a fun2. Ma le altre istanze x, definite in fun1 e globalmente, non sono interessate.

Prima di nested_scopes - in Python pre-2.1 e in 2.1 a meno che non si richieda specificamente la funzionalità utilizzando un'importazione da futuro-futuro - gli ambiti di fun1 e fun2 non sono visibili a fun3, quindi la risposta di S.Lott è valida e si otterrebbe la x globale :

0 0

1

In Python,

qualsiasi variabile a cui è assegnato un valore è locale al blocco in cui appare l'assegnazione.

Se non è possibile trovare una variabile nell'ambito corrente, fare riferimento all'ordine LEGB.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.