Python Language
Ambito e legame variabili
Ricerca…
Sintassi
- globale a, b, c
- non locale a, b
- x = qualcosa # lega x
- (x, y) = qualcosa # lega x e y
- x + = qualcosa # lega x. Allo stesso modo per tutti gli altri "op ="
- del x # lega x
- per x in qualcosa: # si lega x
- con qualcosa come x: # si lega x
- tranne Eccezione come ex: # si lega ex al blocco
Variabili globali
In Python, le variabili all'interno delle funzioni sono considerate locali se e solo se compaiono nella parte sinistra di un'istruzione di assegnazione, o qualche altra occorrenza di associazione; in caso contrario tale associazione viene cercata nel racchiudere le funzioni, fino allo scopo globale. Questo è vero anche se l'istruzione di assegnazione non viene mai eseguita.
x = 'Hi'
def read_x():
print(x) # x is just referenced, therefore assumed global
read_x() # prints Hi
def read_y():
print(y) # here y is just referenced, therefore assumed global
read_y() # NameError: global name 'y' is not defined
def read_y():
y = 'Hey' # y appears in an assignment, therefore it's local
print(y) # will find the local y
read_y() # prints Hey
def read_x_local_fail():
if False:
x = 'Hey' # x appears in an assignment, therefore it's local
print(x) # will look for the _local_ z, which is not assigned, and will not be found
read_x_local_fail() # UnboundLocalError: local variable 'x' referenced before assignment
Normalmente, un'assegnazione all'interno di un oscilloscopio ombreggia qualsiasi variabile esterna con lo stesso nome:
x = 'Hi'
def change_local_x():
x = 'Bye'
print(x)
change_local_x() # prints Bye
print(x) # prints Hi
Dichiarare un nome global
significa che, per il resto dello scope, qualsiasi assegnazione al nome avverrà al livello principale del modulo:
x = 'Hi'
def change_global_x():
global x
x = 'Bye'
print(x)
change_global_x() # prints Bye
print(x) # prints Bye
La parola chiave global
indica che gli assegnamenti avverranno al livello più alto del modulo, non al livello principale del programma. Altri moduli avranno ancora bisogno del solito accesso puntato alle variabili all'interno del modulo.
Riassumendo: per sapere se una variabile x
è locale a una funzione, dovresti leggere l' intera funzione:
- se hai trovato
global x
,x
è una variabile globale - Se hai trovato
nonlocal x
,x
appartiene a una funzione di inclusione e non è né locale né globale - Se hai trovato
x = 5
ofor x in range(3)
o qualche altro legame, allorax
è una variabile locale - Altrimenti
x
appartiene ad un ambito che racchiude (scope della funzione, scope globale o builtin)
Variabili locali
Se un nome è associato a una funzione, per impostazione predefinita è accessibile solo all'interno della funzione:
def foo():
a = 5
print(a) # ok
print(a) # NameError: name 'a' is not defined
I costrutti del flusso di controllo non hanno alcun impatto sull'ambito (ad eccezione di except
), ma l'accesso alla variabile che non è stata ancora assegnata è un errore:
def foo():
if True:
a = 5
print(a) # ok
b = 3
def bar():
if False:
b = 5
print(b) # UnboundLocalError: local variable 'b' referenced before assignment
Operazioni comuni vincolanti assegnazioni, for
cicli e assegnazioni aumentata come a += 5
Variabili non locali
Python 3 ha aggiunto una nuova parola chiave chiamata nonlocal . La parola chiave nonlocale aggiunge un ambito prioritario all'ambito interno. Puoi leggere tutto su di esso in PEP 3104 . Questo è meglio illustrato con un paio di esempi di codice. Uno degli esempi più comuni è creare una funzione che può incrementare:
def counter():
num = 0
def incrementer():
num += 1
return num
return incrementer
Se provi a eseguire questo codice, riceverai un UnboundLocalError perché la variabile num viene referenziata prima che venga assegnata nella funzione più interna. Aggiungiamo nonlocal al mix:
def counter():
num = 0
def incrementer():
nonlocal num
num += 1
return num
return incrementer
c = counter()
c() # = 1
c() # = 2
c() # = 3
Fondamentalmente nonlocal
ti permetterà di assegnare alle variabili in un ambito esterno, ma non un ambito globale. Quindi non è possibile utilizzare non nonlocal
nella nostra funzione counter
perché in tal caso proverebbe ad assegnarli a un ambito globale. Fare un tentativo e si otterrà rapidamente un SyntaxError
. Invece devi usare nonlocal
in una funzione annidata.
(Si noti che la funzionalità presentata qui è implementata meglio usando i generatori).
Evento obbligatorio
x = 5
x += 7
for x in iterable: pass
Ciascuna delle affermazioni precedenti è un'occorrenza di associazione - x
si lega all'oggetto denotato da 5
. Se questa istruzione appare all'interno di una funzione, x
sarà function-local per impostazione predefinita. Vedere la sezione "Sintassi" per un elenco di istruzioni vincolanti.
Le funzioni ignorano l'ambito della classe durante la ricerca dei nomi
Le classi hanno un ambito locale durante la definizione, ma le funzioni all'interno della classe non usano quell'ambito quando cercano i nomi. Poiché lambda sono funzioni e le interpretazioni vengono implementate utilizzando l'ambito della funzione, ciò può portare a comportamenti sorprendenti.
a = 'global'
class Fred:
a = 'class' # class scope
b = (a for i in range(10)) # function scope
c = [a for i in range(10)] # function scope
d = a # class scope
e = lambda: a # function scope
f = lambda a=a: a # default argument uses class scope
@staticmethod # or @classmethod, or regular instance method
def g(): # function scope
return a
print(Fred.a) # class
print(next(Fred.b)) # global
print(Fred.c[0]) # class in Python 2, global in Python 3
print(Fred.d) # class
print(Fred.e()) # global
print(Fred.f()) # class
print(Fred.g()) # global
Gli utenti che non hanno familiarità con il funzionamento di questo scope potrebbero aspettarsi che b
, c
ed e
stampino la class
.
Da PEP 227 :
I nomi nella portata della classe non sono accessibili. I nomi sono risolti nello scope della funzione di chiusura più interna. Se una definizione di classe si verifica in una catena di ambiti nidificati, il processo di risoluzione ignora le definizioni di classe.
Dalla documentazione di Python su denominazione e associazione :
L'ambito dei nomi definiti in un blocco di classe è limitato al blocco di classe; non si estende ai blocchi di codice dei metodi - questo include le comprensioni e le espressioni del generatore poiché sono implementati usando un ambito di funzione. Ciò significa che il seguente non funzionerà:
class A: a = 42 b = list(a + i for i in range(10))
Questo esempio utilizza i riferimenti di questa risposta di Martijn Pieters, che contiene un'analisi più approfondita di questo comportamento.
Il comando
Questo comando ha diverse forme correlate ma distinte.
del v
Se v
è una variabile, il comando del v
rimuove la variabile dal suo ambito. Per esempio:
x = 5
print(x) # out: 5
del x
print(x) # NameError: name 'f' is not defined
Si noti che
del
è un'occorrenza di associazione , il che significa che, a meno che non sia specificato esplicitamente altrimenti (usandononlocal
oglobal
),del v
renderàv
local allo scope corrente. Se si intende eliminarev
in un ambito esterno, utilizzarenonlocal v
global v
oglobal v
nello stesso ambito dell'istruzionedel v
.
In tutto quanto segue, l'intenzione di un comando è un comportamento predefinito, ma non viene applicato dalla lingua. Una classe potrebbe essere scritta in modo tale da invalidare questa intenzione.
del v.name
Questo comando attiva una chiamata a v.__delattr__(name)
.
L'intenzione è di rendere il name
dell'attributo name
disponibile. Per esempio:
class A:
pass
a = A()
a.x = 7
print(a.x) # out: 7
del a.x
print(a.x) # error: AttributeError: 'A' object has no attribute 'x'
del v[item]
Questo comando attiva una chiamata a v.__delitem__(item)
.
L'intenzione è che l' item
non apparterrà alla mappatura implementata dall'oggetto v
. Per esempio:
x = {'a': 1, 'b': 2}
del x['a']
print(x) # out: {'b': 2}
print(x['a']) # error: KeyError: 'a'
del v[a:b]
Questo in realtà chiama v.__delslice__(a, b)
.
L'intenzione è simile a quella descritta sopra, ma con sezioni: intervalli di elementi anziché un singolo elemento. Per esempio:
x = [0, 1, 2, 3, 4]
del x[1:3]
print(x) # out: [0, 3, 4]
Vedi anche Garbage Collection # Il comando del .
Local vs Global Scope
Quali sono gli obiettivi locali e globali?
Tutte le variabili di Python che sono accessibili ad un certo punto nel codice sono in ambito locale o nell'ambito globale .
La spiegazione è che l'ambito locale include tutte le variabili definite nella funzione corrente e l'ambito globale include variabili definite al di fuori della funzione corrente.
foo = 1 # global
def func():
bar = 2 # local
print(foo) # prints variable foo from global scope
print(bar) # prints variable bar from local scope
Si può ispezionare quali variabili sono in quale ambito. Le funzioni built-in locals()
e globals()
restituiscono l'intero scope come dizionari.
foo = 1
def func():
bar = 2
print(globals().keys()) # prints all variable names in global scope
print(locals().keys()) # prints all variable names in local scope
Cosa succede con le scontro sul nome?
foo = 1
def func():
foo = 2 # creates a new variable foo in local scope, global foo is not affected
print(foo) # prints 2
# global variable foo still exists, unchanged:
print(globals()['foo']) # prints 1
print(locals()['foo']) # prints 2
Per modificare una variabile globale, utilizzare la parola chiave global
:
foo = 1
def func():
global foo
foo = 2 # this modifies the global foo, rather than creating a local variable
L'ambito è definito per l'intero corpo della funzione!
Ciò che significa è che una variabile non sarà mai globale per metà della funzione e locale in seguito, o viceversa.
foo = 1
def func():
# This function has a local variable foo, because it is defined down below.
# So, foo is local from this point. Global foo is hidden.
print(foo) # raises UnboundLocalError, because local foo is not yet initialized
foo = 7
print(foo)
Allo stesso modo, la oposite:
foo = 1
def func():
# In this function, foo is a global variable from the begining
foo = 7 # global foo is modified
print(foo) # 7
print(globals()['foo']) # 7
global foo # this could be anywhere within the function
print(foo) # 7
Funzioni all'interno delle funzioni
Possono esserci molti livelli di funzioni annidati all'interno delle funzioni, ma all'interno di una funzione c'è solo un ambito locale per quella funzione e l'ambito globale. Non ci sono ambiti intermedi.
foo = 1
def f1():
bar = 1
def f2():
baz = 2
# here, foo is a global variable, baz is a local variable
# bar is not in either scope
print(locals().keys()) # ['baz']
print('bar' in locals()) # False
print('bar' in globals()) # False
def f3():
baz = 3
print(bar) # bar from f1 is referenced so it enters local scope of f3 (closure)
print(locals().keys()) # ['bar', 'baz']
print('bar' in locals()) # True
print('bar' in globals()) # False
def f4():
bar = 4 # a new local bar which hides bar from local scope of f1
baz = 4
print(bar)
print(locals().keys()) # ['bar', 'baz']
print('bar' in locals()) # True
print('bar' in globals()) # False
global
vs nonlocal
(solo Python 3)
Entrambe queste parole chiave sono utilizzate per ottenere l'accesso in scrittura a variabili che non sono locali alle funzioni correnti.
La parola chiave global
dichiara che un nome dovrebbe essere trattato come una variabile globale.
foo = 0 # global foo
def f1():
foo = 1 # a new foo local in f1
def f2():
foo = 2 # a new foo local in f2
def f3():
foo = 3 # a new foo local in f3
print(foo) # 3
foo = 30 # modifies local foo in f3 only
def f4():
global foo
print(foo) # 0
foo = 100 # modifies global foo
D'altra parte, nonlocal
(vedi Variabili nonlocal
), disponibile in Python 3, prende una variabile locale da un ambito che racchiude l'ambito locale della funzione corrente.
Dalla documentazione Python su nonlocal
:
L'istruzione nonlocale fa sì che gli identificatori elencati facciano riferimento a variabili associate in precedenza nello scope che racchiude il più vicino escludendo le globali.
def f1():
def f2():
foo = 2 # a new foo local in f2
def f3():
nonlocal foo # foo from f2, which is the nearest enclosing scope
print(foo) # 2
foo = 20 # modifies foo from f2!