Python: zmienne globalne - kiedy używać, kiedy unikać?

Logo Pythona i tekst "Zmienne w Pythonie". Obok wąż w czerwonych i fioletowych barwach, symbolizujący zmienne globalne python.

Napisano przez

Alex Jabłoński

Opublikowano

31 mar 2026

Spis treści

Zmienne globalne w Pythonie wydają się proste, dopóki kod nie zaczyna rosnąć i nagle nie wiadomo, skąd bierze się dana wartość oraz kto ją zmienia. W tym tekście pokazuję, jak działa zasięg nazw, kiedy potrzebny jest global, dlaczego łatwo wpaść w pułapkę zasięgu lokalnego i jakie rozwiązania są zwykle lepsze w skryptach oraz aplikacjach webowych. Najważniejsza różnica jest praktyczna: odczyt globalnej wartości to nie to samo co jej modyfikacja.

Najważniejsze rzeczy, które trzeba wiedzieć od razu

  • Globalna nazwa w Pythonie to taka, która jest zdefiniowana na poziomie modułu i może być odczytana z funkcji bez dodatkowych deklaracji.
  • Jeśli chcesz zmienić taką nazwę wewnątrz funkcji, zwykle potrzebujesz global, bo przypisanie domyślnie tworzy nazwę lokalną.
  • Największą pułapką nie jest sam odczyt, tylko sytuacja, w której funkcja przez przypadek zaczyna zasłaniać zmienną zewnętrzną.
  • global działa tylko dla bieżącego modułu, a nie jako uniwersalny skrót do dowolnego stanu w programie.
  • W kodzie produkcyjnym, zwłaszcza webowym, lepiej często przekazać dane jawnie albo zamknąć stan w obiekcie niż polegać na wspólnych zmiennych.

Zrozumienie zakresu zmiennych w Pythonie: warstwy od lokalnej do wbudowanej, w tym zmienne globalne.

Jak działa zasięg i dlaczego lokalna nazwa może przysłonić globalną

Python nie szuka nazw przypadkowo. Najpierw sprawdza zakres lokalny funkcji, potem zakresy otaczające, następnie moduł i dopiero na końcu nazwy wbudowane. To oznacza, że globalna zmienna jest widoczna w funkcji, jeśli tylko ją odczytujesz, ale sytuacja zmienia się w momencie przypisania.

Jeżeli w treści funkcji pojawi się jakiekolwiek przypisanie do danej nazwy, interpreter traktuje ją jako lokalną dla całej funkcji. W praktyce właśnie z tego bierze się większość nieporozumień związanych z globalami.

x = 10

def foo():
    print(x)
    x += 1

Na pierwszy rzut oka wygląda to niewinnie, ale Python uzna x za lokalne, bo w funkcji występuje przypisanie. Gdy dochodzi do print(x), lokalna zmienna jeszcze nie istnieje, więc pojawia się błąd. To nie jest kaprys interpretera, tylko konsekwencja reguły, która ma chronić przed przypadkowym nadpisaniem stanu z zewnątrz.

Warto pamiętać też o jednej ważnej rzeczy: na poziomie modułu wszystkie nazwy są już globalne, więc global nie ma tam sensu. Taka deklaracja ma znaczenie dopiero wewnątrz funkcji, gdy chcesz odwołać się do wiązania z poziomu modułu. To prowadzi nas prosto do pytania, kiedy deklaracja naprawdę jest potrzebna, a kiedy tylko kusi, żeby użyć jej na skróty.

Kiedy potrzebujesz global, a kiedy lepiej go nie używać

Jeżeli funkcja ma tylko odczytać wartość z modułu, niczego nie deklarujesz. Jeżeli ma ją zmienić, wtedy global staje się potrzebne. To prosta zasada, ale właśnie na tym etapie najłatwiej zbudować kod, który działa dziś, a za tydzień jest już trudny do utrzymania.

counter = 0

def read_counter():
    return counter

def increment_counter():
    global counter
    counter += 1

Ten przykład pokazuje podstawowy mechanizm: odczyt nie wymaga dodatkowych instrukcji, ale modyfikacja tak. Sam fakt, że coś można zrobić, nie oznacza jednak, że warto. Ja zwykle traktuję global jako znak, że funkcja zaczyna zarządzać stanem współdzielonym przez cały moduł, a to już wymaga ostrożności.

Gdy problem dotyczy zagnieżdżonych funkcji

Jeśli chcesz zmieniać zmienną z funkcji otaczającej, używasz nonlocal, a nie global. To ważne rozróżnienie, bo oba słowa kluczowe rozwiązują podobny problem, ale w różnych miejscach łańcucha zakresów.

def outer():
    total = 0

    def inner():
        nonlocal total
        total += 1

    inner()
    return total

nonlocal sięga do najbliższego otaczającego zakresu funkcji, a global do modułu. Jeśli pomylisz te dwa mechanizmy, kod może jeszcze wyglądać poprawnie, ale zacznie zachowywać się inaczej niż planowałeś. Gdy już to rozróżnisz, łatwiej przejść do konkretnych przykładów, które zwykle wyjaśniają temat szybciej niż sama definicja.

Przykłady, które najczęściej wyjaśniają temat do końca

Najlepiej rozumie się ten temat przez porównanie trzech sytuacji: tylko odczyt, przypisanie i mutowanie obiektu. Na papierze wyglądają podobnie, ale dla interpretera to zupełnie inne operacje.

Operacja Czy potrzebujesz global Co się dzieje
Odczyt nazwy Nie Python szuka wartości w zewnętrznych zakresach.
x = ... Tak, jeśli chodzi o zmienną z modułu Tworzysz lokalne wiązanie, chyba że użyjesz global.
lista.append(...) Zwykle nie Zmienia się obiekt, ale nie sama nazwa.
lista = lista + [...] Tak Powstaje nowe przypisanie do nazwy.

Odczyt bez przypisania

To najprostszy przypadek. Jeśli w funkcji tylko korzystasz z wartości, globalna nazwa jest widoczna i nie musisz niczego dopisywać. Dzięki temu konfigurację, stałe albo proste liczniki da się odczytać bez dodatkowego szumu w kodzie.

tax_rate = 0.23

def price_with_tax(price):
    return price * (1 + tax_rate)

Ten kod działa, bo tax_rate nie jest w funkcji nadpisywane. W praktyce to jedyny scenariusz, w którym globalne dane bywają naprawdę wygodne. Zaraz obok niego stoi jednak przypadek odwrotny, czyli przypisanie wewnątrz funkcji, które zmienia wszystko.

Przypisanie tworzy lokalną nazwę

Jeśli funkcja ma zmienić wartość z modułu, bez global nie obejdzie się. To właśnie dlatego poniższy kod jest błędny, mimo że wygląda logicznie:

value = 5

def bad():
    print(value)
    value = 6

Python uzna value za lokalne, a wcześniejszy odczyt zakończy się błędem. To jeden z tych momentów, w których początkujący myślą, że interpreter „nie widzi” zmiennej globalnej, a w rzeczywistości interpreter widzi za dużo i stosuje regułę zakresu w sposób konsekwentny. Ostatni przypadek jest jeszcze bardziej mylący, bo dotyczy obiektów mutowalnych.

Mutowanie obiektu nie zawsze wymaga global

Jeżeli zmieniasz zawartość listy albo słownika, ale nie przypisujesz nowej nazwy, global nie jest potrzebny. To dlatego append, extend czy aktualizacja klucza w słowniku działają bez deklaracji:

items = []

def add_item(item):
    items.append(item)

Tu nazwa items nie jest nadpisywana, więc Python nie traktuje jej jako lokalnej. Problem zaczyna się dopiero wtedy, gdy tworzysz nowy obiekt i przypisujesz go do tej samej nazwy. To rozróżnienie między mutowaniem a ponownym wiązaniem nazwy jest jednym z najważniejszych w całym temacie. Gdy je rozumiesz, łatwiej zauważyć typowe błędy zanim staną się usterką w większym projekcie.

Najczęstsze błędy przy pracy ze stanem modułu

W code review najczęściej widzę cztery schematy, które powtarzają się bez względu na poziom doświadczenia. Każdy z nich wygląda na drobny detal, ale później potrafi kosztować godzinę debugowania.

  • UnboundLocalError - funkcja czyta nazwę, a potem przypisuje do niej wartość, więc Python uznaje ją za lokalną od samego początku.
  • Spóźniony global - deklaracja pojawia się po pierwszym użyciu nazwy w tym samym zakresie, co kończy się błędem składniowym.
  • Ukryte współdzielenie stanu - kilka funkcji modyfikuje ten sam obiekt, ale nikt nie ma jasnego właściciela odpowiedzialności.
  • Globalny stan w aplikacji webowej - przy wielu procesach lub wątkach wartość może nie być wspólna tam, gdzie się tego spodziewasz, a dane requestowe nie powinny i tak żyć w module.
name = "Ala"

def broken():
    print(name)
    global name
    name = "Ola"

Ten przykład pokazuje jeszcze jedną rzecz: global musi pojawić się zanim cokolwiek zrobisz z nazwą w danym zakresie. Jeśli użyjesz jej wcześniej, interpreter zatrzyma program. Dodatkowy problem pojawia się wtedy, gdy zmienna globalna ma nazwę podobną do wbudowanej funkcji, na przykład list albo id. Taki wybór nie psuje programu od razu, ale skutecznie utrudnia czytanie i debugowanie kodu.

Jeśli pracujesz nad aplikacją webową, dochodzi jeszcze kwestia współbieżności. Globalny licznik albo koszyk w pamięci procesu nie jest dobrym magazynem danych między żądaniami, bo każdy worker może mieć własną kopię, a równoległe zapisy wymagają synchronizacji. To dobry moment, żeby porównać globalny stan z rozwiązaniami, które zwykle składają się lepiej w większych projektach.

Lepsze alternatywy dla globali w realnym kodzie

W praktyce najczęściej wybieram jedną z kilku dróg: przekazuję dane jawnie, zwracam wynik albo zamykam stan w obiekcie. Każda z tych opcji ma swój koszt, ale też daje znacznie lepszą kontrolę nad przepływem danych niż rozproszony stan modułu.

Rozwiązanie Kiedy działa najlepiej Co zyskujesz Ograniczenie
Argumenty funkcji Gdy dane są wejściem do obliczenia Jawność i prostsze testy Więcej parametrów w podpisie funkcji
Zwracanie wyniku Gdy funkcja ma coś policzyć lub przetworzyć Mniej ukrytych efektów ubocznych Trzeba czasem przebudować wywołania
Obiekt lub dataclass Gdy stan należy do jednego procesu biznesowego Porządek i jedna odpowiedzialność Wymaga trochę więcej struktury
Moduł konfiguracji Gdy wartości są stałe lub zmieniają się rzadko Centralne miejsce na ustawienia Nie nadaje się do dynamicznego stanu użytkownika
Baza danych, cache, sesja Gdy stan musi przetrwać dłużej niż proces Trwałość i współdzielenie między instancjami Większa złożoność i koszt infrastruktury

Jeśli chcesz zachować prostotę, ale uniknąć globali, często wystarcza mały obiekt z jawnie przekazywanym stanem:

from dataclasses import dataclass

@dataclass
class AppState:
    counter: int = 0

def increment(state: AppState):
    state.counter += 1

Taki układ jest czytelniejszy, bo od razu widać, kto odpowiada za dane. W małym skrypcie może to wyglądać na nadmiarowy formalizm, ale w projekcie, który będzie rósł, zwykle zwraca się bardzo szybko. Z tego miejsca już łatwo przejść do pytania, kiedy global w ogóle da się obronić.

Kiedy global ma sens i jak trzymać go w ryzach

Nie demonizuję globali absolutnie. W małym skrypcie, jednorazowym narzędziu albo prostym module pomocniczym mogą być rozsądnym skrótem, jeśli spełniają kilka warunków.

  • Stan ma jednego właściciela i jest modyfikowany w jednym miejscu.
  • Wartość nie zależy od pojedynczego żądania użytkownika ani od równoległych zadań.
  • Zmiana stanu jest mała, przewidywalna i łatwa do przetestowania.
  • Kod nadal pozostaje czytelny bez zgadywania, skąd bierze się dana wartość.

Gdy któryś z tych punktów przestaje być prawdziwy, global przestaje być wygodnym skrótem, a zaczyna być technicznym długiem. Wtedy lepiej przenieść stan do obiektu, przekazać go jako argument albo wynieść poza proces, jeśli ma być współdzielony szerzej.

Moja praktyczna zasada jest prosta: jeśli po miesiącu nie potrafię spojrzeć na funkcję i od razu powiedzieć, kto może zmienić jej dane wejściowe, to prawdopodobnie mam za dużo ukrytego stanu. Właśnie dlatego w Pythonie globalne zmienne traktuję jako narzędzie pomocnicze, a nie domyślny sposób projektowania kodu.

FAQ - Najczęstsze pytania

Zmienne globalne to nazwy zdefiniowane na poziomie modułu, dostępne do odczytu w dowolnym miejscu tego modułu. Mogą być modyfikowane w funkcjach za pomocą słowa kluczowego `global`.

`global` jest potrzebne, gdy chcesz zmodyfikować wartość zmiennej globalnej wewnątrz funkcji. Bez niego przypisanie stworzy zmienną lokalną, która przysłoni globalną.

Nie. Jeśli funkcja tylko odczytuje wartość zmiennej globalnej i jej nie modyfikuje, słowo kluczowe `global` nie jest potrzebne. Python automatycznie szuka nazw w nadrzędnych zakresach.

`global` odnosi się do zmiennej zdefiniowanej na poziomie modułu. `nonlocal` służy do modyfikowania zmiennych z najbliższego otaczającego zakresu funkcji, ale nie z zakresu globalnego.

Lepsze praktyki to przekazywanie danych jako argumenty funkcji, zwracanie wyników, hermetyzacja stanu w obiektach (np. `dataclass`) lub korzystanie z baz danych/cache dla trwałego stanu.

Oceń artykuł

Ocena: 0.00 Liczba głosów: 0

Tagi:

zmienne globalne python zmienne globalne python kiedy używać python global vs nonlocal zasięg zmiennych python modyfikacja zmiennej globalnej w funkcji python

Udostępnij artykuł

Alex Jabłoński

Alex Jabłoński

Nazywam się Alex Jabłoński i od 9 lat zajmuję się programowaniem webowym. Moja przygoda z tą dziedziną zaczęła się od prostych projektów, które z czasem przerodziły się w pasję do tworzenia użytecznych i estetycznych aplikacji internetowych. Fascynuje mnie nie tylko sam proces kodowania, ale także to, jak technologie wpływają na nasze życie i jak możemy je wykorzystać, aby rozwiązywać codzienne problemy. Piszę o różnych aspektach programowania, od podstawowych języków po bardziej zaawansowane techniki i narzędzia. Staram się, aby moje teksty były przystępne i zrozumiałe, a skomplikowane zagadnienia przedstawiam w prosty sposób. Regularnie śledzę nowinki w branży, co pozwala mi dostarczać aktualne i rzetelne informacje. Moim celem jest nie tylko edukacja, ale także inspirowanie innych do rozwijania swoich umiejętności w programowaniu.

Napisz komentarz