❄️ Zakres zmiennych
Zakres zmiennych (scope)
Każda zmienna w Pythonie istnieje w określonym zakresie (ang. scope) - obszarze kodu, w którym jest dostępna. Python wyszukuje nazwy zmiennych według reguły LEGB:
- Local - wnętrze bieżącej funkcji,
- Enclosing - funkcja zewnętrzna (dla funkcji zagnieżdżonych),
- Global - poziom modułu (skryptu),
- Built-in - wbudowane nazwy Pythona, np.
print,len.
x = "globalny"
def zewnetrzna():
x = "zewnętrzny"
def wewnetrzna():
x = "lokalny"
print(x) # lokalny
wewnetrzna()
print(x) # zewnętrzny
zewnetrzna()
print(x) # globalny
Python szuka x zawsze od najwęższego zakresu ku najszerszemu i zatrzymuje się przy pierwszym trafieniu.
Słowo kluczowe global
Wewnątrz funkcji przypisanie do nazwy zawsze tworzy nową zmienną lokalną, nawet jeśli istnieje zmienna globalna o tej samej nazwie. Żeby zmodyfikować zmienną globalną, trzeba ją jawnie zadeklarować słowem global.
licznik = 0
def zwieksz():
licznik += 1 # UnboundLocalError! Python widzi przypisanie i traktuje licznik jako lokalny
zwieksz()
Poprawne rozwiązanie:
licznik = 0
def zwieksz():
global licznik
licznik += 1
zwieksz()
zwieksz()
print(licznik) # 2
Ogranicz użycie global
Zmienne globalne utrudniają testowanie i śledzenie przepływu danych. Zamiast nich lepiej przekazywać wartości przez argumenty i zwracać przez return. global może być uzasadniony np. dla liczników lub flag w małych skryptach.
Słowo kluczowe nonlocal
nonlocal działa jak global, ale dotyczy zmiennej z najbliższej otaczającej funkcji (nie globalnej). Jest niezbędne, gdy chcemy zmodyfikować zmienną z funkcji zewnętrznej wewnątrz funkcji zagnieżdżonej.
def zewnetrzna():
licznik = 0
def wewnetrzna():
nonlocal licznik
licznik += 1
wewnetrzna()
wewnetrzna()
print(licznik) # 2
zewnetrzna()
Bez nonlocal przypisanie licznik += 1 wewnątrz wewnetrzna rzuciłoby UnboundLocalError, bo Python potraktowałby licznik jako zmienną lokalną.
📝 Zadania
Stwórz plik python1course/zaj02/zakres_zmiennych.py i wykonaj w nim poniższe zadania.
-
Uruchom poniższy kod i wyjaśnij wynik. Następnie popraw go tak, żeby funkcja
resetujrzeczywiście zerowała licznik globalny.licznik = 10 def resetuj(): licznik = 0 resetuj() print(licznik) -
Napisz funkcję
stworz_licznik(), która używanonlocaldo zarządzania wewnętrznym stanem. Funkcja powinna zwracać dwie inne funkcje:zwiekszipobierz, które odpowiednio inkrementują i odczytują wewnętrzny licznik.Przykład użycia:
zwieksz, pobierz = stworz_licznik() zwieksz() zwieksz() zwieksz() print(pobierz()) # 3 -
Napisz funkcję
stworz_konto(saldo_poczatkowe), która zwraca trzy funkcje:wplac(kwota),wyplac(kwota)isprawdz(). Każda z nich operuje na wewnętrznym saldzie konta poprzeznonlocal. Funkcjawyplacpowinna wypisać ostrzeżenie, jeśli saldo byłoby ujemne po wypłacie.wplac, wyplac, sprawdz = stworz_konto(100) wplac(50) wyplac(30) print(sprawdz()) # 120 wyplac(200) # Ostrzeżenie: niewystarczające środki