📦 Zmienne (Variables)

Poznaliśmy już typy wartości (str, int, float, bool) oraz operacje na nich. Ale zamiast pisać `"Witaj " + "Michał"`, łatwiej jest zapamiętać imię gracza, by witać go w całej grze!

Zmienna to takie "pudełko" w pamięci RAM...
👩‍🏫
Z odpowiednią etykietą, żebym wiedział co jest w środku!
🧑‍💻
Zmienna to nazwany obszar w pamięci komputera. Tworzymy ją podając nazwę, używając pojedynczego znaku przypisania = i wpisując obok wartość.

🏷️ Tworzenie i wywoływanie

Aby włożyć coś do pudełka, po prostu używamy znaku równości.

a = 10
print(a)              # Wydrukuje zawartość pudełka 'a', czyli 10

b = "witaj"
print(b)              # Wydrukuje: witaj
Znak przypisania = ma niewiele wspólnego z operatorem porównania ==.
  • == zadaje pytanie "czy są równe?" (Prawda/Fałsz).
  • = to rozkaz "Od teraz ty jesteś tym!" (Przypisanie).

📝 Zmienne a teksty

Wywoływanie pudełka a wpisywanie surowego tekstu to dwie różne rzeczy, na które musimy uważać.

imie = "Marcin"

# Bez cudzysłowów - wywołujemy pudełko!
print(imie)               # Wypisze: Marcin

# Z cudzysłowami - wypisujemy zwykły tekst.
print("imie")               # Wypisze słowo: imie
Jeśli zapomnisz cudzysłowu przy tworzeniu zmiennej np. imie = Marcin, Python spróbuje znaleźć w systemie INNE pudełko o nazwie "Marcin". Jeśli go nie ma, program wyrzuci błąd: NameError: name 'Marcin' is not defined.

🧮 Obliczenia na zmiennych

Na zmiennych można wykonywać te same operacje, co na surowych danych. Komputer w locie otwiera pudełka i podmienia je w głowie na ukryte w nich wartości.

x = 10
y = 20
print(x + y)       # Wynik: 30
a = "Hello "
b = "World"
print(a + b)    # Hello World

Możemy oczywiście łączyć zmienną ze zwykłą wartością prosto w print, np: print(a + "Borys"), co da nam "Hello Borys".

♻️ Zmienianie wartości (Nadpisywanie)

Zmienne potrafią się ZMIENIAĆ w trakcie trwania programu. Nowa wartość wypiera starą.

punkty = 10
print(punkty)         # Wypisze: 10

punkty = 25
print(punkty)         # Wypisze: 25
Co więcej, Python to język "dynamicznie typowany". Możesz w jednej linii napisać punkty = 10, a pięć linijek niżej wrzucić tam tekst: punkty = "Koniec gry" i Python nie zgłosi błędu (choć inni programiści na widok takiego misz-maszu byliby wściekli!).

➕ Aktualizacja wartości

Często nie chcemy nadpisywać całego pudełka nową liczbą, ale ZMODYFIKOWAĆ stary stan (np. przy leczeniu bohatera, chcemy DODAĆ mu 10hp do tego, co ma teraz).

hp = 100
hp = hp - 20         # Otrzymałeś cios za 20
print(hp)             # Zostało 80
Pamiętaj, by czytać w programowaniu zawsze od PRAWEJ STRONY znaku =.
1. Najpierw robi się prawa strona: wyjmuje 100 z pudełka i odejmuje 20.
2. Wynik z prawej (80), wrzucany jest na siłę z powrotem do pudełka `hp` po lewej!

🚀 Skrócone operatory

Ciągłe pisanie x = x + 1 bywa nużące. Stworzono do tego wygodne skróty. Nazywamy to "cukrem syntaktycznym" - słodzi życie koderom!

# Tradycyjnie:
kasa = kasa + 50
hp = hp - 15
# Forma skrócona (+=):
kasa += 50
hp -= 15

Działa to także dla mnożenia (*=), dzielenia (/=) oraz modulo (%=). Są to niezwykle popularne operatory w pętlach i grach!

🎯 Ćwiczenie: Symulacja Pamięci

+150 XP

Pudełka w grze

Przeanalizuj poniższy kod w głowie (linijka po linijce). Jaka będzie ostateczna wartość w zmiennej score, która zostanie wydrukowana na samym końcu?

score = 0
points_per_coin = 10

# Gracz zebrał 3 monety:
score += points_per_coin * 3

# Gracz stracił trochę punktów:
score -= 5

print(score)

✅ Rozwiązanie: Wynik gry

Linia 1 i 2:
Pudełko score to 0, pudełko mnożnika to 10.

Linia 3: score += points_per_coin * 3
Wykonujemy mnożenie 10 * 3 = 30. Następnie += wsypuje tę wartość do score. Zmienna score wynosi teraz 30.

Linia 4: score -= 5
Z 30 zabieramy 5. Ostateczna wartość w pudełku to 25.

Ostateczny wynik na ekranie: 25!

📜 Zasady nazywania zmiennych

Nie możemy nazywać naszych pudełek jak nam się podoba. Python posiada sztywne reguły, które wyłapią błędy.

Programiści niepisanie zadeklarowali: nie używamy polskich znaków! Mimo że kod `żółć = 5` w nowym Pythonie zadziała, utrudnia to pracę kolegom zza granicy i robi brzydkie błędy na starych serwerach.

🔤 Konwencja snake_case

Skoro nie możemy w zmiennych używać spacji, w jaki sposób oddzielić od siebie słowa, żeby nazwa szybkoscracingcara była czytelniejsza dla oka?

W Pythonie oficjalnie przyjętym standardem jest snake_case ("pisownia wężowa").

Wszystkie litery są w niej MAŁE, a zamiast spacji wstawiamy znak podkreślenia `_`.

# Dobrze (snake_case):
szybkosc_racing_cara = 200

# Źle (wymieszane, nieczytelne):
SzybkoscRacingCara = 200
Inne języki mogą mieć swoje zasady. Np. w JavaScript czy C# używa się konwencji camelCase (pisownia wielbłądzia), gdzie słowa łączymy w jedno, a wielka litera udaje garb na grzbiecie: szybkoscRacingCara = 200. W Pythonie pełzamy snake_casem!

🎯 Ćwiczenie: Poprawność zmiennych

+50 XP

Zadanie Wzrokowe

Które z poniższych nazw zmiennych są błędne (wywołają krytyczny błąd w kodzie), a które po prostu są brzydkie i łamią niepisaną zasadę snake_case?

  1. moj_samochod
  2. 2_gracz
  3. punkty gracza
  4. imie_1
  5. bardzoSzybkiSamochod

Zastanów się zanim przewiniesz dalej!

✅ Rozwiązanie: Poprawność zmiennych

  1. moj_samochod     ➔ Idealnie poprawna.
  2. 2_gracz          ➔ BŁĄD (nie można zaczynać od cyfry!)
  3. punkty gracza    ➔ BŁĄD (spacja jest zakazana!)
  4. imie_1           ➔ Poprawna (cyfry w środku i na końcu są ok).
  5. bardzoSzybki... ➔ Zadziała, ale łamie konwencję (snake_case).

🔁 Przypisywanie Kaskadowe (Wielokrotne)

Python oferuje małe skróty, jeśli chcesz szybko stworzyć dużo zmiennych na start gry (np. resetowanie wyniku dla wszystkich 3 graczy do zera).

# Zamiast w 3 linijkach:
a = b = c = 0

print(a, b, c)           # Wypisze: 0 0 0

Kierunek działania przypisania: od najbardziej z prawej strony do lewej. Komputer najpierw widzi 0 i wrzuca do `c`, potem zawartość `c` wrzuca do `b`, potem zawartość `b` do `a`.

🤝 Rozpakowywanie (Tuple Unpacking)

Jeśli masz więcej wartości do przypisania o różnym typie, możesz wypisać je po przecinku z lewej, a potem podać tyle samo odpowiedzi po prawej stronie! Nazywa się to wielokrotnym przypisaniem.

# Wypakowujemy pakiet wartości:
wiek, imie, czy_zyje = 20, "Jacek", True

print(imie)                   # Wypisze: Jacek
Zasada: Ilość wartości po prawej stronie równania musi IDEALNIE odpowiadać ilości nazw zmiennych po lewej stronie, inaczej Python rzuci błąd `ValueError` (not enough values to unpack).

🤹 Zamiana zmiennych (Swap)

Składnia po przecinku ma genialne zastosowanie w przypadku chęci ZAMIANY wartości miejscami (swap).

# INNY JĘZYK (np. C++):
# (Potrzebna 3 pusta zmienna by nic nie znikło!)
tmp = a
a = b
b = tmp
# W PYTHONIE:
a = 1
b = 2

a, b = b, a       # SWAP!
W Pythonie robi się to w JEDNEJ linijce bez wymyślania tymczasowych zmiennych! To świetny trick oszczędzający sporo nerwów podczas sortowania list.

👻 Specjalna wartość: None (Nic)

Czasem chcemy stworzyć etykietę i przygotować w pamięci RAM pudełko (np. stworzyć bohatera, który ma w statystykach imie), ale jeszcze na starcie gry... nie znamy jego nazwy (czeka na uzupełnienie).

None (Nic/Brak) to specjalna wartość symbolizująca pustkę. Służy najczęściej jako "zaślepka" do nowo tworzonych zmiennych. Zaczyna się z WIELKIEJ litery.
nick = None                   # Na razie nie mamy nicka

nick = "SmoczyWojownik"       # Gracz wpisał. Zastępujemy None!

🔎 Sprawdzanie Czystości: 'is None'

Aby sprawdzić czy nasze pudełko nadal zieje pustką (bo np. gracz go nie uzupełnił w oknie logowania), nie powinno używać się standardowego operatora równości ==.

name = None

# Python zaleca słówko IS (czy fizycznie jest to "Nic"):
print(name is None)             # Wypisze: True

name = "Marcin"
print(name is None)             # Wypisze: False
Zasada: `is` to potężniejszy operator niż `==`. Patrzy on na to, czy obiekty pochodzą z dokładnie tego samego miejsca w pamięci. Ponieważ pustka `None` jest tylko jedna na cały system, zawsze używamy `is None`!

🤯 Wymazywanie pudełek z istnienia

Zmienne zajmują miejsce w RAM (nawet bardzo mało). Czasem chcemy się pozbyć wartości całkowicie, niszcząc ją bezpowrotnie (tzw. zbiórka śmieci przez komputer).

Aby to zrobić, po prostu wrzucamy nową pustkę z powrotem do starego pudełka.

secret = "PoufneDane!"
print(secret)

# Zacieramy ślady:
secret = None

Ciekawostka: w Pythonie działa tzw. Garbage Collector (Śmieciarka). Jeśli do Twojej wartości ("PoufneDane") nie ma już podpiętej ŻADNEJ zmiennej ani strzałki... Śmieciarka Pythona zauważy to, i wymaże ją z istnienia na komputerze całkowicie za Ciebie.

🏆 Podsumowanie Odkryć

Oto zestaw najpotężniejszych trików dotyczących zmiennych, które opanowaliśmy dzisiaj:

Koncepcja Przykład (Python) Co robi?
Operatory Skrócone hp -= 20 Zmniejsza obecną wartość o podaną z prawej strony i nadpisuje stan.
Przypisanie Wielokrotne a = b = c = 0 Ustawia wszystkim zmiennym jedną i tę samą początkową wartość.
Rozpakowywanie & Swap a, b = b, a Zamienia zmienne błyskawicznie miejscami (np. w sortowaniu).
Brak Danych None Wartość uroczyście oznaczająca "Pustkę". Testowana operatorem "is None".

Część I za nami! Został nam potężny temat "Mutowalności" pamięci...

🔮 Pamięć i Mutowalność (Wstęp do Części II)

Musimy teraz zgłębić bardzo ważny problem tego, jak zmienne przechowują wartości w pamięci. Zrozumienie tego jest kluczowe, aby unikać ukrytych i wrednych błędów (bugów) niszczących gry.

Zmienna tak naprawdę nie jest samym pudełkiem z wartością. Zmienna to tylko strzałka (odnośnik) wskazująca na miejsce w wielkiej szafie pamięci RAM, w którym ten obiekt leży.

Podstawowe typy które dziś poznaliśmy (int, float, bool, str) są tak zwanymi obiektami niemutowalnymi (immutable). Ich wnętrza nie da się modyfikować po stworzeniu. Można jedynie stworzyć nową wartość w nowym miejscu w szafie i przepiąć strzałkę.

🧬 Bezpieczne ksero (Niemutowalne)

Spójrzmy na ten z pozoru łatwy kawałek kodu z liczbami:

a = 10
b = a                # Zmienna B kopiuje to, na co wskazuje A

a = 20                # Strzałka A przepina się na nową liczbę 20

print("a=", a)         # a == 20
print("b=", b)         # b == 10
Dla podstawowych typów (liczby, teksty), przypisanie zmiennej do innej zmiennej przypomina bezpieczne ksero. Zmiana jednej z nich (a = 20) w żaden sposób nie psuje tej drugiej (b zostaje przy 10).

🗃️ Typy Mutowalne - Zapowiedź Chaosu

Nie wszystkie elementy języka są tak bezpieczne. Z czasem zaczniemy używać w Pythonie "złożonych" struktur danych. Jedną z najczęstszych są Listy (tablice ekwipunku, zbiory danych).

# Listę ekwipunku tworzymy za pomocą nawiasów kwadratowych:
eq = ["Miecz", "Tarcza"]
Lista to tak zwany typ mutowalny (mutable). To oznacza, że komputer może modyfikować wnętrze istniejącego zbioru (np. dosypywać do niego złoto na żywo) bez konieczności przepinania strzałki do nowego miejsca w szafie.

😱 Problem ze współdzieloną listą

Co się stanie, jeśli skopiujemy zmienną wskazującą na listę? Zamiast "ksera", dostaniemy wspólne drzwi do tego samego pokoju!

lista_a = [1, 2]
lista_b = lista_a          # Strzałka B wskazuje NA TO SAMO pudełko co strzałka A

# .append(3) wsypuje element '3' prosto do wnętrza!
lista_a.append(3)

print(lista_a)              # Wypisze: [1, 2, 3]
print(lista_b)              # Wypisze... TEŻ [1, 2, 3]!
Wiele osób łapie tu błędy. Chcą "zapisać stan" w drugiej zmiennej, modyfikują pierwszą z nich, a za chwilę okazuje się, że kopia zapasowa też się zepsuła. Wartość mutowalna (np. lista) nie kopiuje się sama z siebie!

✅ Jak bezpiecznie kserować listy? (Rozwiązanie)

Aby naprawić ten problem i stworzyć fizyczną KOPIĘ pudełka z nowym adresem, musimy wymusić na Pythonie skserowanie (skopiowanie) całej listy za pomocą dodatkowej komendy, np: .copy().

# Opcja 1: Użycie copy()
lista_a = [1, 2]
lista_b = lista_a.copy()

lista_a.append(3)

print(lista_a) # [1, 2, 3]
print(lista_b) # [1, 2] Ocalona!
# Opcja 2: Slicing [:]
lista_a = [1, 2]
lista_b = lista_a[:]

lista_a.append(3)

print(lista_a) # [1, 2, 3]
print(lista_b) # [1, 2] Ocalona!

To najbardziej ustrzeże Cię przed wrednymi błędami współdzielenia danych w złożonych programach!