Python Language
Variabel bereik en binding
Zoeken…
Syntaxis
- globaal a, b, c
- niet-lokaal a, b
- x = iets # bindt x
- (x, y) = iets # bindt x en y
- x + = iets # bindt x. Zo ook voor alle andere "op ="
- del x # bindt x
- voor x in iets: # bindt x
- met iets als x: # bindt x
- behalve Uitzondering als ex: # bindt ex binnen blok
Globale variabelen
In Python worden variabelen in functies als lokaal beschouwd als en alleen als ze aan de linkerkant van een toewijzingsinstructie verschijnen, of een andere bindende gebeurtenis; anders wordt een dergelijke binding opgezocht in omsluitende functies, tot aan de globale scope. Dit geldt zelfs als de toewijzingsopdracht nooit wordt uitgevoerd.
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
Normaal gesproken zal een toewijzing binnen een scope eventuele externe variabelen met dezelfde naam overschaduwen:
x = 'Hi'
def change_local_x():
x = 'Bye'
print(x)
change_local_x() # prints Bye
print(x) # prints Hi
Door een naam global
verklaren, worden voor de rest van de scope toewijzingen aan de naam op het hoogste niveau van de module uitgevoerd:
x = 'Hi'
def change_global_x():
global x
x = 'Bye'
print(x)
change_global_x() # prints Bye
print(x) # prints Bye
Het global
trefwoord betekent dat toewijzingen op het hoogste niveau van de module plaatsvinden, niet op het hoogste niveau van het programma. Andere modules zullen nog steeds de gebruikelijke gestippelde toegang tot variabelen binnen de module nodig hebben.
Samenvattend: om te weten of een variabele x
lokaal is voor een functie, moet u de hele functie lezen:
- als je
global x
hebt gevonden, dan isx
een globale variabele - Als u
nonlocal x
hebt gevonden, behoortx
tot een omsluitende functie en is noch lokaal noch globaal - Als je
x = 5
offor x in range(3)
of een andere binding hebt gevonden, dan isx
een lokale variabele - Anders behoort
x
tot een omsluitend bereik (functiebereik, algemeen bereik of ingebouwde)
Lokale variabelen
Als een naam binnen een functie is gebonden , is deze standaard alleen toegankelijk binnen de functie:
def foo():
a = 5
print(a) # ok
print(a) # NameError: name 'a' is not defined
Besturingsstroom constructen hebben geen invloed op de omvang (behalve except
), maar de toegang variabele die nog niet is toegewezen een fout:
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
Veelgebruikte bindbewerkingen zijn toewijzingen, for
lussen en uitgebreide toewijzingen zoals a += 5
Niet-lokale variabelen
Python 3 heeft een nieuw trefwoord toegevoegd met de naam nonlocal . Het niet-lokale trefwoord voegt een overschrijving aan het binnenbereik toe. Je kunt er alles over lezen in PEP 3104 . Dit wordt het best geïllustreerd met een paar codevoorbeelden. Een van de meest voorkomende voorbeelden is om een functie te maken die kan worden verhoogd:
def counter():
num = 0
def incrementer():
num += 1
return num
return incrementer
Als u deze code probeert uit te voeren, ontvangt u een UnboundLocalError omdat naar de variabele num wordt verwezen voordat deze in de binnenste functie wordt toegewezen. Laten we niet-lokaal aan de mix toevoegen:
def counter():
num = 0
def incrementer():
nonlocal num
num += 1
return num
return incrementer
c = counter()
c() # = 1
c() # = 2
c() # = 3
In principe kunt u nonlocal
variabelen in een extern bereik toewijzen, maar geen algemeen bereik. U kunt dus niet- nonlocal
niet gebruiken in onze counter
omdat het dan zou proberen toe te wijzen aan een globale scope. Probeer het eens en u krijgt snel een SyntaxError
. In plaats daarvan moet u nonlocal
in een geneste functie.
(Merk op dat de hier gepresenteerde functionaliteit beter is geïmplementeerd met behulp van generatoren.)
Bindend voorkomen
x = 5
x += 7
for x in iterable: pass
Elk van de bovenstaande uitspraken is bindend - x
wordt gebonden aan het object aangegeven met 5
. Als deze instructie in een functie wordt weergegeven, is x
standaard functie-lokaal. Zie de sectie "Syntaxis" voor een lijst met bindende verklaringen.
Functies slaan klassebereik over bij het opzoeken van namen
Klassen hebben een lokaal bereik tijdens de definitie, maar functies binnen de klasse gebruiken dat bereik niet bij het opzoeken van namen. Omdat lambda's functies zijn en begrippen worden geïmplementeerd met behulp van functiebereik, kan dit tot verrassend gedrag leiden.
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
Gebruikers die niet weten hoe deze scope werkt, kunnen verwachten dat b
, c
en e
class
afdrukken.
Van PEP 227 :
Namen in klassebereik zijn niet toegankelijk. Namen worden omgezet in het binnenste omringende functiebereik. Als een klassedefinitie voorkomt in een reeks geneste bereiken, slaat het resolutieproces klassedefinities over.
Uit de documentatie van Python over naamgeving en binding :
Het bereik van namen gedefinieerd in een klasseblok is beperkt tot het klasseblok; het strekt zich niet uit tot de codeblokken van methoden - dit omvat begrip en generatoruitdrukkingen, aangezien deze worden geïmplementeerd met behulp van een functiebereik. Dit betekent dat het volgende mislukt:
class A: a = 42 b = list(a + i for i in range(10))
Dit voorbeeld gebruikt referenties uit dit antwoord van Martijn Pieters, die een meer diepgaande analyse van dit gedrag bevat.
Het del commando
Deze opdracht heeft verschillende gerelateerde maar toch verschillende vormen.
del v
Als v
een variabele is, verwijdert de opdracht del v
de variabele uit het bereik. Bijvoorbeeld:
x = 5
print(x) # out: 5
del x
print(x) # NameError: name 'f' is not defined
Merk op dat
del
een bindende gebeurtenis is , wat betekent dat tenzijdel v
expliciet anders wordt vermeld (gebruikmakend vannonlocal
ofglobal
),del v
ervoor zorgt datv
lokaal is voor de huidige scope. Als uv
in een externe scope wilt verwijderen, gebruikt unonlocal v
ofglobal v
in dezelfde scope als de instructiedel v
.
In het volgende is de intentie van een opdracht een standaardgedrag, maar wordt niet afgedwongen door de taal. Een klasse kan zo worden geschreven dat deze intentie ongeldig wordt.
del v.name
Deze opdracht v.__delattr__(name)
een aanroep naar v.__delattr__(name)
.
De bedoeling is om het attribuut te maken name
onbeschikbaar. Bijvoorbeeld:
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]
Deze opdracht v.__delitem__(item)
een aanroep naar v.__delitem__(item)
.
De bedoeling is dat het item
niet thuishoort in de afbeelding die door het object v
geïmplementeerd. Bijvoorbeeld:
x = {'a': 1, 'b': 2}
del x['a']
print(x) # out: {'b': 2}
print(x['a']) # error: KeyError: 'a'
del v[a:b]
Dit roept eigenlijk v.__delslice__(a, b)
.
De bedoeling is vergelijkbaar met die hierboven beschreven, maar met plakjes - reeksen items in plaats van een enkel item. Bijvoorbeeld:
x = [0, 1, 2, 3, 4]
del x[1:3]
print(x) # out: [0, 3, 4]
Zie ook Garbage Collection # De opdracht del .
Lokaal versus wereldwijd bereik
Wat is lokaal en wereldwijd bereik?
Alle variabelen van Python die op enig moment in de code toegankelijk zijn, bevinden zich in lokaal bereik of in globaal bereik .
De verklaring is dat het lokale bereik alle variabelen omvat die in de huidige functie zijn gedefinieerd en het globale bereik omvat variabelen die buiten de huidige functie zijn gedefinieerd.
foo = 1 # global
def func():
bar = 2 # local
print(foo) # prints variable foo from global scope
print(bar) # prints variable bar from local scope
Men kan inspecteren welke variabelen zich in welke scope bevinden. Ingebouwde functies locals()
en globals()
geven de hele scopes terug als woordenboeken.
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
Wat gebeurt er met naamconflicten?
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
Gebruik trefwoord global
om een globale variabele te wijzigen:
foo = 1
def func():
global foo
foo = 2 # this modifies the global foo, rather than creating a local variable
Het bereik is gedefinieerd voor het hele lichaam van de functie!
Wat het betekent is dat een variabele nooit de helft van de functie en daarna lokaal zal zijn, of vice versa.
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)
Evenzo is het tegenovergestelde:
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
Functies binnen functies
Er kunnen veel functieniveaus zijn genest in functies, maar binnen elke functie is er slechts één lokale scope voor die functie en de globale scope. Er zijn geen tussenbereiken.
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
(alleen Python 3)
Beide trefwoorden worden gebruikt om schrijftoegang te krijgen tot variabelen die niet lokaal zijn voor de huidige functies.
Het global
sleutelwoord verklaart dat een naam als een globale variabele moet worden behandeld.
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
Aan de andere kant neemt nonlocal
(zie Niet- lokale variabelen ), beschikbaar in Python 3, een lokale variabele van een omsluitend bereik in het lokale bereik van de huidige functie.
Uit de Python-documentatie op nonlocal
:
De niet-lokale instructie zorgt ervoor dat de weergegeven id's verwijzen naar eerder gebonden variabelen in het dichtstbijzijnde omsluitende bereik exclusief globalen.
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!