1Czym jest Python?

Python - przyjazny język programowania

Python to jeden z najpopularniejszych języków programowania na świecie. Został stworzony przez Guido van Rossuma i wydany po raz pierwszy w lutym 1991 roku. Nazwa nie pochodzi od węża, lecz od brytyjskiej grupy komediowej Monty Python.

Dlaczego warto uczyć się Pythona?

  • Łatwy do nauki - Ma prostą, przejrzystą składnię
  • Wszechstronny - Używany w wielu dziedzinach
  • Popularny - Ma ogromną społeczność i wiele bibliotek
  • Potężny - Nadaje się do prostych skryptów i zaawansowanych projektów

Python jest idealny dla osób, które dopiero zaczynają przygodę z programowaniem!

+------------------+
|                  |
|   Witaj w        |
|   Świecie        |
|   Pythona!       |
|                  |
|   print("Hello!")|
|                  |
+------------------+
            

Python to język programowania wysokiego poziomu, który od ponad trzech dekad zdobywa coraz większą popularność dzięki swojej czytelnej składni i szerokim możliwościom zastosowania. Został zaprojektowany z myślą o czytelności kodu, co przejawia się między innymi w wymuszaniu wcięć do wyznaczania bloków kodu zamiast nawiasów klamrowych znanych z innych języków. Ta cecha sprawia, że kod napisany w Pythonie jest łatwy do zrozumienia zarówno dla autora, jak i dla innych programistów, co ma ogromne znaczenie w projektach zespołowych. W przeciwieństwie do języków kompilowanych, takich jak C++ czy Java, Python jest językiem interpretowanym, co oznacza, że kod jest wykonywany linia po linii bez potrzeby uruchamiania osobnego etapu kompilacji.

Wszechstronność Pythona sprawia, że znajduje on zastosowanie w niemal każdej dziedzinie informatyki, od prostych skryptów automatyzujących codzienne zadania, przez analizę danych i uczenie maszynowe, aż po tworzenie zaawansowanych aplikacji internetowych. W środowisku IT Python jest szczególnie ceniony za możliwość szybkiego prototypowania i bogaty ekosystem bibliotek, które rozwiązują większość typowych problemów bez konieczności pisania kodu od zera. Dla studentów kierunków informatycznych Python stanowi idealny pierwszy język programowania, ponieważ pozwala skupić się na logice algorytmów zamiast na zawiłościach składniowych.

2Gdzie używamy Pythona?

Zastosowania Pythona

Python jest wykorzystywany praktycznie wszędzie. Oto najważniejsze obszary:

  • Automatyzacja i skrypty - Automatyzacja zadań, przetwarzanie plików, zarządzanie systemami
  • Analiza danych i sztuczna inteligencja - Pandas, NumPy, TensorFlow, PyTorch
  • Tworzenie stron internetowych - Django, Flask, Pyramid
  • Testowanie oprogramowania - Automatyzacja testów, pytest
  • Bezpieczeństwo komputerowe - Pisanie narzędzi do testów penetracyjnych
  • Edukacja - Idealny do nauki programowania

W tym kursie...

Skupimy się na praktycznym użyciu Pythona do tworzenia skryptów automatyzujących pracę z systemami operacyjnymi.

+-------------------------+
|   PYTHON W DZIAŁANIU    |
+-------------------------+
| [x] Tworzenie skryptów |
| [x] Automatyzacja IT    |
| [x] Analiza plików      |
| [x] Praca z siecią      |
| [x] Przetwarzanie danych|
+-------------------------+
            

Zastosowania Pythona w praktyce IT są tak różnorodne, że trudno znaleźć obszar informatyki, w którym nie byłby on używany. W dziedzinie automatyzacji Python pozwala na pisanie skryptów do zarządzania plikami, wykonywania kopii zapasowych, monitorowania systemów i integracji z API różnych usług. W analizie danych biblioteki takie jak Pandas i NumPy umożliwiają przetwarzanie ogromnych zbiorów danych w czasie rzeczywistym, a frameworki do uczenia maszynowego, takie jak TensorFlow i PyTorch, są standardem w branży sztucznej inteligencji.

Tworzenie stron internetowych przy użyciu frameworków Django i Flask to kolejny popularny kierunek, który pozwala na szybkie budowanie skalowalnych aplikacji webowych. W testowaniu oprogramowania Python oferuje zaawansowane narzędzia, takie jak pytest i Selenium, które automatyzują proces weryfikacji poprawności kodu. W bezpieczeństwie komputerowym Python jest używany do pisania narzędzi do testów penetracyjnych, analizy malware i automatyzacji zadań związanych z cyberbezpieczeństwem. W trakcie tego kursu skupimy się przede wszystkim na praktycznym wykorzystaniu Pythona do automatyzacji zadań administracyjnych w systemach operacyjnych.

3Jak zainstalować Pythona?

Pobieranie i instalacja

Python jest dostępny za darmo dla wszystkich systemów operacyjnych.

  • Windows - Pobierz instalator z python.org i uruchom
  • Linux - Zazwyczaj już zainstalowany. Sprawdź: python3 --version
  • macOS - Pobierz z python.org lub użyj Homebrew: brew install python3

Weryfikacja instalacji

Po instalacji sprawdź, czy Python działa. Otwórz terminal i wpisz:

python --version lub python3 --version

Zobaczysz numer wersji, np. Python 3.11.4

# Windows - otwórz CMD lub PowerShell
C:\> python --version
Python 3.11.4

# Linux/macOS - otwórz terminal
$ python3 --version
Python 3.11.4

# Uruchomienie interaktywnego Pythona
$ python3
>>> print("Witaj!")
Witaj!
>>> exit()

Instalacja Pythona w systemie Windows jest niezwykle prosta i sprowadza się do pobrania instalatora z oficjalnej strony python.org oraz uruchomienia go z zaznaczeniem opcji dodania Pythona do zmiennej PATH. Bez zaznaczenia tej opcji interpreter Pythona nie będzie dostępny z poziomu wiersza poleceń, co znacząco utrudni pracę. W systemach Linux i macOS Python jest często preinstalowany, ale warto sprawdzić jego wersję za pomocą polecenia `python3 --version` i w razie potrzeby zaktualizować go do najnowszej stabilnej wersji.

Po instalacji warto zweryfikować poprawność konfiguracji, uruchamiając w terminalu polecenie `python --version` (Windows) lub `python3 --version` (Linux/macOS). Jeśli wszystko zostało skonfigurowane prawidłowo, zobaczysz numer zainstalowanej wersji, na przykład `Python 3.12.0`. Edytor IDLE dołączony do instalacji Pythona jest wystarczający do nauki podstaw, ale w profesjonalnej pracy zaleca się używanie zaawansowanych środowisk programistycznych, takich jak PyCharm, VS Code czy nawet zwykłego notatnika z podświetlaniem składni w połączeniu z terminalem.

4Tworzymy pierwszy skrypt!

Krok po kroku

Stwórzmy razem nasz pierwszy program w Pythonie. Będzie to klasyczny "Hello World" - pierwszy program każdego programisty.

  1. Otwórz edytor tekstu (np. Notatnik, VS Code, PyCharm)
  2. Napisz poniższy kod:
  3. Zapisz plik jako moj_pierwszy_skrypt.py
  4. Uruchom go w terminalu: python moj_pierwszy_skrypt.py

Co oznaczają te linie?

  • # To jest komentarz - Linia którą Python ignoruje
  • print("Tekst") - Wyświetla tekst na ekranie
# To jest nasz pierwszy skrypt w Pythonie
# Komentarze zaczynają się od znaku #

print("Witaj świecie!")
print("To jest mój pierwszy program.")
print("Python jest super!")

Pierwszy skrypt w Pythonie to tradycyjny program wypisujący tekst powitalny `print("Hello, World!")`, który od pokoleń służy programistom na całym świecie do sprawdzenia, czy środowisko jest poprawnie skonfigurowane. W Pythonie funkcja `print()` wyświetla przekazany jej argument w konsoli i jest podstawowym narzędziem do komunikacji z użytkownikiem. Aby utworzyć skrypt, wystarczy otworzyć dowolny edytor tekstu, napisać kod i zapisać plik z rozszerzeniem `.py`, na przykład `hello.py`.

Nazwa pliku skryptu w Pythonie może zawierać tylko litery, cyfry i podkreślenia, a rozszerzenie `.py` informuje system operacyjny, że plik zawiera kod w języku Python. W odróżnieniu od języków kompilowanych, skrypty Pythonowe nie wymagają osobnego etapu budowania i mogą być uruchamiane bezpośrednio po zapisaniu. Dla początkujących programistów najprostszym sposobem pracy jest pisanie kodu w edytorze, zapisywanie pliku i uruchamianie go w terminalu za pomocą polecenia `python nazwa_pliku.py`.

5Jak uruchamiać skrypty Python?

Metody uruchamiania

Istnieje kilka sposobów na uruchomienie kodu Python:

  1. Z pliku (najczęściej używana metoda):
    python nazwa_pliku.py
  2. Tryb interaktywny (do nauki i testowania):
    python - otwiera konsolę Pythona
  3. Z IDE (np. PyCharm, VS Code) - naciśnij przycisk Run

Python 2 vs Python 3

Uwaga: W tym kursie używamy Python 3. Python 2 jest już przestarzały. Dlatego używamy poleceń python3 zamiast python.

# Uruchomienie skryptu z pliku
$ python3 moj_skrypt.py
Witaj!
Mój pierwszy skrypt działa!

# Tryb interaktywny - wpisuj kod linijka po linijce
$ python3
>>> 2 + 2
4
>>> print("Cześć!")
Cześć!
>>> nazwa = "Python"
>>> print(f"Witaj, {nazwa}!")
Witaj, Python!
>>> exit()

Uruchamianie skryptów Pythonowych w systemie Windows może odbywać się na kilka sposobów, z których najprostszym jest wpisanie polecenia `python nazwa_pliku.py` w wierszu poleceń lub PowerShell. Jeśli Python został poprawnie dodany do zmiennej PATH, interpreter uruchomi się automatycznie i wykona kod zawarty w pliku. W systemach Linux i macOS komenda różni się nieznacznie i przyjmuje postać `python3 nazwa_pliku.py`, ponieważ równolegle mogą być zainstalowane różne wersje Pythona.

Nowoczesne środowiska programistyczne IDE oferują wygodniejsze metody uruchamiania, takie jak kliknięcie przycisku Run czy użycie skrótu klawiszowego, który automatycznie zapisuje plik i uruchamia go w zintegrowanym terminalu. Niezależnie od wybranej metody, uruchomienie skryptu powoduje wykonanie wszystkich instrukcji od góry do dołu, linia po linii, aż do napotkania końca pliku lub wywołania `sys.exit()`. Zrozumienie tego przepływu wykonania jest kluczowe dla pisania poprawnych i przewidywalnych programów w Pythonie.

6Zmienne - skrzynki na dane

Co to jest zmienna?

Zmienna to jak skrzynka z etykietą. W skrzynce możesz coś umieścić (np. liczbę lub tekst), a etykieta (nazwa zmiennej) pozwala Ci to znaleźć później.

Tworzenie zmiennych w Pythonie

W Pythonie tworzenie zmiennej jest bardzo proste. Po prostu wpisz nazwę, znak = i wartość:

  • wiek = 20 - zmienna przechowuje liczbę
  • imie = "Anna" - zmienna przechowuje tekst
  • jest_studentem = True - zmienna przechowuje prawdę/fałsz

Nazewnictwo zmiennych

Nazwy zmiennych powinny:

  • Zaczynać się od litery lub podkreślenia
  • Nie zawierać spacji (używaj _ zamiast)
  • Być opisowe (np. wiek_studenta zamiast x)
# Tworzenie zmiennych
wiek = 20
imie = "Anna"
jest_studentem = True
wzrost = 1.65

# Wyświetlanie zmiennych
print(wiek)
print(imie)
print(jest_studentem)
print(wzrost)

# Użycie zmiennych
print("Mam na imię", imie)
print("Mam", wiek, "lat")
print("Czy jestem studentem?", jest_studentem)

Zmienne w Pythonie są dynamicznie typowane, co oznacza, że interpreter automatycznie określa typ zmiennej na podstawie przypisanej wartości, bez konieczności jawnej deklaracji typu. Deklaracja zmiennej odbywa się za pomocą prostego przypisania `nazwa = wartość`, na przykład `wiek = 21` tworzy zmienną o nazwie `wiek` z wartością liczbową 21. Nazwy zmiennych mogą zawierać litery, cyfry i podkreślenia, ale muszą zaczynać się od litery lub podkreślenia, a wielkość liter ma znaczenie: `imie` i `Imie` to dwie różne zmienne.

Python oferuje możliwość przypisania wartości wielu zmiennym jednocześnie za pomocą składni `a, b, c = 1, 2, 3`, co jest szczególnie przydatne przy zwracaniu wielu wartości z funkcji. Zmienne w Pythonie są referencjami do obiektów w pamięci, co oznacza, że przypisanie `b = a` nie kopiuje wartości, ale tworzy nową referencję do tego samego obiektu. Zrozumienie mechanizmu referencji jest ważne przy pracy z obiektami mutowalnymi (mutable), takimi jak listy czy słowniki, gdzie modyfikacja przez jedną zmienną wpływa na wszystkie referencje wskazujące na ten sam obiekt.

7Typy danych - liczby

Typy liczbowe w Pythonie

Python rozpoznaje różne rodzaje liczb automatycznie:

  • int (integer) - liczby całkowite: 1, 42, -7, 0
  • float - liczby dziesiętne: 3.14, -0.5, 2.0

Podstawowe operacje matematyczne

  • + - dodawanie
  • - - odejmowanie
  • * - mnożenie
  • / - dzielenie (zawsze daje float)
  • // - dzielenie całkowite
  • % - reszta z dzielenia (modulo)
  • ** - potęgowanie
# Liczby całkowite (int)
a = 10
b = 3

# Operacje
print("a + b =", a + b)    # 13
print("a - b =", a - b)    # 7
print("a * b =", a * b)    # 30
print("a / b =", a / b)    # 3.333...
print("a // b =", a // b)  # 3 (część całkowita)
print("a % b =", a % b)    # 1 (reszta)
print("a ** b =", a ** b)  # 1000 (10 do potęgi 3)

# Float - liczby dziesiętne
cena = 19.99
podatek = 0.23
print("Cena z podatkiem:", cena * (1 + podatek))

Typy liczbowe w Pythonie obejmują kilka podstawowych kategorii, z których najważniejsze to liczby całkowite `int` i zmiennoprzecinkowe `float`. Liczby całkowite w Pythonie mogą być dowolnie duże, co odróżnia ten język od wielu innych, gdzie zakres liczb całkowitych jest ograniczony architekturą procesora. Liczby zmiennoprzecinkowe służą do reprezentacji wartości ułamkowych i są implementowane zgodnie ze standardem IEEE 754, co może prowadzić do subtelnych błędów zaokrągleń przy precyzyjnych obliczeniach finansowych.

Python oferuje także typ `complex` do liczb zespolonych, który jest używany głównie w zastosowaniach naukowych i inżynieryjnych. Sprawdzenie typu zmiennej jest możliwe za pomocą wbudowanej funkcji `type()`, która zwraca informację o typie obiektu. Konwersja między typami liczbowymi odbywa się za pomocą funkcji `int()` i `float()`, które przyjmują wartość do konwersji jako argument. Należy pamiętać, że konwersja liczby zmiennoprzecinkowej na całkowitą obcina część ułamkową, a nie zaokrągla jej.

8Typy danych - tekst (stringi)

String - tekst w Pythonie

Tekst w Pythonie nazywamy stringiem (skrót: str). Tekst zawsze umieszczamy w cudzysłowach:

  • Podwójne cudzysłowy: "To jest tekst"
  • Pojedyncze cudzysłowy: 'To też jest tekst'

Łączenie tekstów

Teksty można łączyć za pomocą + lub f-stringów (zalecane):

Najważniejsze operacje na tekstach

  • .upper() - zamienia na wielkie litery
  • .lower() - zamienia na małe litery
  • .strip() - usuwa białe znaki
  • len(tekst) - zwraca długość tekstu
# Tworzenie stringów
imie = "Anna"
nazwisko = 'Kowalska'

# Łączenie tekstów
pelne_imie = imie + " " + nazwisko
print(pelne_imie)  # Anna Kowalska

# F-stringi (nowoczesne łączenie)
wiek = 20
print(f"Nazywam się {imie} {nazwisko} i mam {wiek} lat.")

# Metody stringów
print(imie.upper())      # ANNA
print(imie.lower())     # anna
print(len(imie))        # 4

# Indeksowanie (znak po znaku)
print(imie[0])  # A (pierwszy znak)
print(imie[-1]) # a (ostatni znak)

Łańcuchy znaków w Pythonie, nazywane stringami, są podstawowym typem danych do przechowywania i manipulacji tekstem. Stringi można tworzyć zarówno przy użyciu pojedynczych cudzysłowów `'tekst'`, jak i podwójnych `"tekst"`, co jest szczególnie przydatne, gdy tekst zawiera apostrofy lub cudzysłowy. Dla tekstów wieloliniowych Python oferuje potrójne cudzysłowy `'''tekst'''` lub `"""tekst"""`, które pozwalają na zachowanie formatowania i podziału na linie bez używania znaków nowej linii.

Stringi w Pythonie są niemutowalne, co oznacza, że po utworzeniu nie można zmienić ich zawartości, a każda operacja modyfikacji tworzy nowy obiekt string. Do łączenia stringów służy operator `+`, a do powielania operator `*`, na przykład `"Ha" * 3` tworzy tekst `"HaHaHa"`. Dostęp do pojedynczych znaków stringa odbywa się za pomocą indeksów w nawiasach kwadratowych, gdzie pierwszy znak ma indeks 0, a ostatni można uzyskać za pomocą indeksu -1. Python oferuje także zaawansowane mechanizmy wycinania fragmentów stringa (slicing) za pomocą składni `tekst[poczatek:koniec:krok]`.

9Typy danych - wartości logiczne (True/False)

Boolean - prawda i fałsz

Typ boolean (skrót: bool) przyjmuje tylko dwie wartości:

  • True - prawda (1)
  • False - fałsz (0)

Operatory porównania

Służą do sprawdzania warunków:

  • == - równe
  • != - różne
  • > - większe
  • < - mniejsze
  • >= - większe lub równe
  • <= - mniejsze lub równe

Operatory logiczne

  • and - i (oba muszą być True)
  • or - lub (przynajmniej jeden musi być True)
  • not - nie (odwraca wartość)
# Wartości logiczne
czy_jestem_studentem = True
czy_mam_duzo_pieniedzy = False

print(czy_jestem_studentem)  # True
print(czy_mam_duzo_pieniedzy)  # False

# Operatory porównania
x = 10
y = 5

print(x == y)   # False (równe?)
print(x != y)   # True (różne?)
print(x > y)    # True (większe?)
print(x < y)    # False (mniejsze?)

# Operatory logiczne
wiek = 20
ma_dowod = True

# AND - oba warunki muszą być spełnione
print(wiek >= 18 and ma_dowod)  # True

# OR - przynajmniej jeden warunek
print(wiek < 18 or ma_dowod)    # True

# NOT - odwraca
print(not wiek < 18)  # True (bo 20 nie jest < 18)

Wartości logiczne w Pythonie są reprezentowane przez typ `bool`, który przyjmuje jedną z dwóch wartości: `True` lub `False`. Wartości te są używane przede wszystkim w instrukcjach warunkowych, pętlach oraz wszędzie tam, gdzie potrzebne jest podjęcie decyzji na podstawie spełnienia określonego warunku. Python traktuje wiele wartości jako fałszywe w kontekście logicznym, między innymi: `None`, `0`, `0.0`, puste stringi `""`, puste listy `[]` i puste słowniki `{}`. Wszystkie inne wartości są uznawane za prawdziwe.

Operatory porównania w Pythonie zwracają wartości logiczne i obejmują `==` (równość), `!=` (nierówność), `>` i `<` (większość/mniejszość) oraz `>=` i `<=` (większość/mniejszość lub równość). Operatory logiczne `and`, `or` i `not` pozwalają na łączenie i negację warunków, przy czym Python stosuje leniwą ewaluację: w przypadku `and` drugi warunek jest sprawdzany tylko wtedy, gdy pierwszy jest prawdziwy. Funkcja `bool()` pozwala na jawną konwersję dowolnej wartości na typ logiczny według reguł opisanych powyżej.

10Komentarze - dokumentowanie kodu

Co to są komentarze?

Komentarze to tekst w kodzie, który Python całkowicie ignoruje. Służą do:

  • Wyjaśniania co robi dany fragment kodu
  • Dokumentowania ważnych decyzji
  • Tymczasowego wyłączania kodu (debugowanie)

Rodzaje komentarzy

  • Jednoliniowe - zaczynają się od #
  • Wieloliniowe - tekst między """ lub '''

Porada

Pisz komentarze mądrze! Dobry komentarz wyjaśnia dlaczego coś robisz, a nie co robisz (to widać w kodzie).

# To jest komentarz jednoliniowy
# Python go zignoruje

print("Ten kod się wykona")

# ---
# KOMENTARZ WIELOLINIOWY
# (używany często jako dokumentacja funkcji)
# ---

"""
To jest komentarz
rozpoczynający się od trzech
podwójnych cudzysłowów.
"""

'''
Albo trzech pojedynczych.
'''

# Praktyczny przykład:
# Pobieram dane od użytkownika
imie = input("Podaj swoje imię: ")

# Sprawdzam, czy użytkownik jest pełnoletni
wiek = int(input("Ile masz lat? "))

Komentarze w Pythonie są nieodzownym elementem profesjonalnego kodu, który znacząco ułatwia jego zrozumienie i utrzymanie w dłuższej perspektywie. Komentarz jednoliniowy rozpoczyna się od znaku `#` i obejmuje całą resztę linii, co pozwala na szybkie opisywanie pojedynczych instrukcji. Komentarze wieloliniowe można uzyskać przez użycie potrójnych cudzysłowów, które formalnie są stringami, ale jeśli nie są przypisane do zmiennej, są ignorowane przez interpreter i pełnią funkcję komentarza blokowego.

Docstringi stanowią specjalną formę komentarzy w Pythonie, które są zapisywane jako pierwsza instrukcja w funkcji, klasie lub module i służą do tworzenia dokumentacji. Docstringi są dostępne w czasie wykonania za pomocą atrybutu `__doc__` i mogą być wyświetlane przez narzędzia takie jak `help()`. W profesjonalnych projektach stosuje się konwencję PEP 257, która określa zasady formatowania docstringów. Dobrą praktyką jest pisanie komentarzy wyjaśniających dlaczego kod robi to, co robi, a nie co robi, ponieważ sama składnia Pythona jest na tyle czytelna, że cel instrukcji jest zwykle oczywisty.

11Funkcja print() - wyświetlanie tekstu

Funkcja print()

print() to podstawowa funkcja do wyświetlania informacji użytkownikowi. Może wyświetlać tekst, liczby i wartości zmiennych.

Jak używać print()

  • print("Tekst") - wyświetla tekst
  • print(zmienna) - wyświetla wartość zmiennej
  • print("Tekst", zmienna) - łączy tekst i zmienną
  • print(f"Zmienna x = {x}") - f-string (formatowanie)

Dodatkowe opcje

  • sep='-' - separator między elementami
  • end=' ' - znak kończący (domyślnie nowa linia)
# Podstawowe użycie
print("Witaj, świecie!")
print(42)
print(3.14)

# Zmienne
imie = "Anna"
wiek = 20
print(imie)
print("Mam", wiek, "lat")

# F-string - nowoczesne formatowanie
print(f"Nazywam się {imie} i mam {wiek} lat.")

# Separator
print("jabłko", "banan", "pomarańcza", sep=", ")
# Wynik: jabłko, banan, pomarańcza

# Bez nowej linii (end)
print("Punkt 1", end=" -> ")
print("Punkt 2", end=" -> ")
print("Koniec")
# Wynik: Punkt 1 -> Punkt 2 -> Koniec

# Formatowanie liczb
cena = 19.99
print(f"Cena: {cena:.2f} zł")
print(f"Procent: {0.1234:.1%}")

Funkcja `print()` jest podstawowym narzędziem wyjścia w Pythonie, które służy do wyświetlania danych w konsoli. Najprostsze użycie `print("Hello")` wypisuje podany tekst, ale funkcja ta oferuje znacznie więcej możliwości dzięki parametrom `sep` i `end`. Parametr `sep` określa separator między przekazanymi argumentami, domyślnie jest to spacja, na przykład `print("A", "B", "C", sep="-")` wyświetli `A-B-C`. Parametr `end` określa, co zostanie wypisane na końcu linii, domyślnie jest to znak nowej linii `\n`.

Możliwość zmiany parametru `end` na pusty string `""` lub inny znak pozwala na budowanie złożonych formatowań wyjścia bez konieczności łączenia stringów. Funkcja `print()` przyjmuje dowolną liczbę argumentów i automatycznie konwertuje je na stringi przed wyświetleniem, co oznacza, że można przekazywać bezpośrednio liczby czy inne typy danych. W nowszych wersjach Pythona funkcja `print()` obsługuje także formatowanie f-string, co pozwala na wygodne wstawianie wartości zmiennych bezpośrednio w tekście.

12Funkcja input() - pobieranie danych od użytkownika

Funkcja input()

input() służy do pobierania danych od użytkownika. Program zatrzymuje się i czeka, aż użytkownik wpisze tekst i naciśnie Enter.

Podstawowy wzór

zmienna = input("Wiadomość dla użytkownika: ")

Konwersja typów

Funkcja input() zawsze zwraca string (tekst). Aby otrzymać liczbę, musisz użyć konwersji:

  • int(input()) - zamienia na liczbę całkowitą
  • float(input()) - zamienia na liczbę dziesiętną
# Pobieranie tekstu
print("Jak masz na imię?")
imie = input()
print(f"Cześć, {imie}!")

# Z parametrem - wiadomość w jednej linii
nazwisko = input("Podaj swoje nazwisko: ")
print(f"Razem: {imie} {nazwisko}")

# Pobieranie liczby (konwersja!)
wiek_text = input("Ile masz lat? ")
wiek = int(wiek_text)  # Zamiana tekstu na liczbę
print(f"Za rok będziesz miał {wiek + 1} lat")

# Krótszy zapis (w jednej linii)
cena = float(input("Podaj cenę produktu: "))
podatek = cena * 0.23
print(f"Cena z podatkiem: {cena + podatek:.2f} zł")

# Przykład z kalkulatorem
a = float(input("Podaj pierwszą liczbę: "))
b = float(input("Podaj drugą liczbę: "))
print(f"Suma: {a + b}")

Funkcja `input()` służy do pobierania danych od użytkownika w trakcie działania programu i jest podstawowym mechanizmem interakcji z konsolą. Po wywołaniu `input()` program wstrzymuje swoje działanie i czeka, aż użytkownik wpisze tekst i zatwierdzi go klawiszem Enter. Opcjonalny argument funkcji to tekst podpowiedzi, który jest wyświetlany użytkownikowi przed polem wprowadzania, na przykład `input("Podaj swoje imię: ")`. Ważną cechą funkcji `input()` jest to, że zawsze zwraca tekst typu `str`, niezależnie od tego, czy użytkownik wpisał liczbę, datę czy inny typ danych.

Konwersja pobranego tekstu na odpowiedni typ jest obowiązkiem programisty i najczęściej wykonuje się ją przez bezpośrednie rzutowanie za pomocą funkcji `int()` lub `float()`. Jeśli użytkownik wpisze wartość, która nie może zostać przekonwertowana, Python zgłosi wyjątek `ValueError`, który należy obsłużyć w bloku `try-except`. W praktyce warto zawsze walidować dane wejściowe przed ich użyciem, co zapobiega awaryjnemu zakończeniu programu z powodu nieprawidłowych danych wprowadzonych przez użytkownika.

13Operacje na tekstach (stringach)

Manipulowanie tekstem

Stringi mają wiele użytecznych metod, które pozwalają je przekształcać i analizować:

Najważniejsze metody

  • .upper() / .lower() - zamiana wielkości liter
  • .strip() - usuwa białe znaki z początku/końca
  • .replace(stary, nowy) - zamienia fragment tekstu
  • .split(separator) - dzieli tekst na listę
  • in - sprawdza, czy tekst zawiera fragment

Indeksowanie i krojenie (slicing)

Możesz pobrać fragment tekstu używając indeksów:

  • tekst[0] - pierwszy znak
  • tekst[2:5] - znaki od 2 do 4
  • tekst[-1] - ostatni znak
tekst = "  Witaj, Świecie!  "

# Usuwanie białych znaków
print(tekst.strip())  # "Witaj, Świecie!"

# Wielkość liter
print(tekst.upper())
print(tekst.lower())

# Zamiana
print(tekst.replace("Witaj", "Cześć"))

# Sprawdzanie zawartości
print("Witaj" in tekst)   # True
print("Python" in tekst)   # False

# Dzielenie tekstu
zdanie = "Python jest super"
slowa = zdanie.split(" ")
print(slowa)  # ['Python', 'jest', 'super']

# Indeksowanie
print(tekst[0])     # ' ' (pierwszy znak)
print(tekst[1:8])   # 'Witaj, '
print(tekst[-4:-1]) # 'cie'
print(tekst[::2])   # co drugi znak

Operacje na stringach w Pythonie są niezwykle bogate i obejmują zarówno podstawowe funkcje, jak i zaawansowane metody przetwarzania tekstu. Metoda `upper()` zamienia wszystkie litery na wielkie, `lower()` na małe, a `capitalize()` zamienia pierwszą literę tekstu na wielką, pozostawiając pozostałe małe. Metoda `strip()` usuwa białe znaki z początku i końca tekstu, co jest szczególnie przydatne przy czyszczeniu danych wejściowych od użytkownika lub z plików.

Metoda `split()` dzieli tekst na listę podciągów według podanego separatora, na przykład `"a,b,c".split(",")` zwraca `['a', 'b', 'c']`, a jej przeciwieństwem jest metoda `join()`, która łączy elementy listy w jeden string. Metoda `replace()` zamienia wszystkie wystąpienia podanego fragmentu na inny, a `find()` wyszukuje pozycję podtekstu w stringu. Operator `in` pozwala na szybkie sprawdzenie, czy dany fragment występuje w tekście, zwracając wartość logiczną. Opanowanie tych metod jest kluczowe dla efektywnego przetwarzania danych tekstowych w Pythonie.

14Listy - kolekcje danych

Co to jest lista?

Lista to uporządkowany zbiór elementów. Możesz myśleć o niej jak o liście zakupów - każdy element ma swoje miejsce.

Tworzenie listy

Listę tworzymy używając nawiasów kwadratowych []:

  • liczby = [1, 2, 3, 4, 5]
  • imiona = ["Anna", "Bartek", "Celina"]
  • mieszane = [1, "tekst", True, 3.14]

Dostęp do elementów

Elementy numerujemy od 0:

  • lista[0] - pierwszy element
  • lista[1] - drugi element
  • lista[-1] - ostatni element
# Tworzenie list
owoce = ["jabłko", "banan", "wiśnia"]
liczby = [1, 2, 3, 4, 5]
pusta = []

print(owoce)
print(liczby)
print(f"Mam {len(owoce)} owoce na liście")

# Dostęp do elementów (indeksowanie od 0!)
print(owoce[0])   # jabłko (pierwszy)
print(owoce[1])   # banan (drugi)
print(owoce[-1])  # wiśnia (ostatni)
print(owoce[-2])  # banan (przedostatni)

# Zmiana elementu
owoce[0] = "gruszka"
print(owoce)

# Sprawdzanie czy element istnieje
print("banan" in owoce)  # True
print("pomarańcza" in owoce)  # False

# Dodawanie elementów
owoce.append("pomarańcza")
print(owoce)

Listy w Pythonie są jednym z najważniejszych i najczęściej używanych typów danych, służących do przechowywania kolekcji elementów w określonej kolejności. Listę tworzy się za pomocą nawiasów kwadratowych z elementami oddzielonymi przecinkami, na przykład `owoce = ["jabłko", "banan", "pomarańcza"]`. Listy w Pythonie są dynamiczne, co oznacza, że mogą zmieniać swój rozmiar w trakcie działania programu, a także mogą zawierać elementy różnych typów, na przykład mieszając liczby z tekstami.

Indeksowanie list rozpoczyna się od zera, a dostęp do elementów odbywa się przez podanie indeksu w nawiasach kwadratowych, na przykład `owoce[0]` zwraca pierwszy element. Python oferuje także indeksy ujemne, gdzie `-1` oznacza ostatni element, `-2` przedostatni i tak dalej, co znacznie ułatwia dostęp do elementów z końca kolekcji. Liczbę elementów w liście zwraca funkcja `len()`, która jest jedną z najczęściej używanych funkcji wbudowanych w Pythonie. Listy można dowolnie zagnieżdżać, tworząc struktury wielowymiarowe, na przykład listę list reprezentującą macierz.

15Operacje na listach

Dodawanie elementów

  • lista.append(element) - dodaje na końcu
  • lista.insert(index, element) - wstawia w określone miejsce
  • lista.extend([a, b, c]) - dodaje wiele elementów

Usuwanie elementów

  • lista.remove(element) - usuwa pierwsze wystąpienie
  • lista.pop() - usuwa i zwraca ostatni element
  • lista.pop(index) - usuwa i zwraca element z danego indeksu
  • del lista[index] - usuwa element (nie zwraca)

Inne operacje

  • len(lista) - liczba elementów
  • lista.sort() - sortuje (zmienia listę!)
  • sorted(lista) - zwraca posortowaną kopię
# Lista startowa
nums = [3, 1, 4, 1, 5, 9, 2, 6]

# Dodawanie
nums.append(10)
nums.insert(0, 100)

# Usuwanie
nums.remove(1)
ostatni = nums.pop()
print(f"Usunięty: {ostatni}")

# Sortowanie
nums.sort()
print(f"Posortowane: {nums}")

posortowane = sorted(nums, reverse=True)
print(f"Odwrotnie: {posortowane}")

# List slicing (krojenie)
cyfry = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(cyfry[2:5])   # [2, 3, 4]
print(cyfry[:4])    # [0, 1, 2, 3]
print(cyfry[5:])    # [5, 6, 7, 8, 9]
print(cyfry[::2])   # [0, 2, 4, 6, 8] co drugi

Operacje na listach w Pythonie obejmują bogaty zestaw metod do dodawania, usuwania i modyfikowania elementów. Metoda `append(element)` dodaje nowy element na koniec listy, `insert(indeks, element)` wstawia element na określoną pozycję, a `extend(lista)` dodaje wszystkie elementy z innej listy na koniec bieżącej. Do usuwania elementów służą metody `remove(element)` usuwająca pierwsze wystąpienie wartości, `pop(indeks)` usuwająca i zwracająca element o podanym indeksie, oraz `clear()` czyszcząca całą listę.

Sortowanie listy odbywa się za pomocą metody `sort()`, która modyfikuje listę w miejscu, lub funkcji `sorted()`, która zwraca nową posortowaną listę, pozostawiając oryginał bez zmian. Odwrócenie kolejności elementów umożliwia metoda `reverse()`. Operator `in` sprawdza, czy dany element znajduje się w liście, co jest przydatne przy walidacji danych. Listy w Pythonie są obiektami mutable, co oznacza, że wszystkie opisane operacje modyfikują istniejący obiekt listy, a nie tworzą jego kopii, co ma istotne znaczenie dla wydajności przy pracy z dużymi zbiorami danych.

16Słowniki - dane w parach klucz:wartość

Co to jest słownik?

Słownik przechowuje dane w parach klucz: wartość. Możesz myśleć o nim jak o prawdziwym słowniku - podajesz hasło (klucz), dostajesz definicję (wartość).

Tworzenie słownika

Używamy nawiasów klamrowych {}:

slownik = {"klucz1": "wartość1", "klucz2": "wartość2"}

Dostęp do danych

  • slownik["klucz"] - pobiera wartość
  • slownik.get("klucz", "domyślna") - bezpieczny dostęp
  • "klucz" in slownik - sprawdza czy istnieje
# Tworzenie słownika
student = {
    "imie": "Anna",
    "nazwisko": "Kowalska",
    "wiek": 20,
    "kierunek": "Informatyka"
}

print(student)
print(student["imie"])

# Bezpieczny dostęp z domyślną wartością
telefon = student.get("telefon", "brak danych")
print(f"Telefon: {telefon}")

# Dodawanie/modyfikacja
student["telefon"] = "123-456-789"
student["wiek"] = 21
print(student)

# Iteracja po słowniku
for klucz in student:
    print(f"{klucz}: {student[klucz]}")

# Bardziej elegancko:
for klucz, wartosc in student.items():
    print(f"{klucz}: {wartosc}")

# Usuwanie
del student["telefon"]
print(student)

Słowniki w Pythonie to struktury danych przechowujące pary klucz-wartość, gdzie każdy klucz jest unikalny i służy do szybkiego dostępu do powiązanej z nim wartości. Słownik tworzy się za pomocą nawiasów klamrowych z parami oddzielonymi dwukropkiem, na przykład `osoba = {"imie": "Anna", "wiek": 25, "miasto": "Warszawa"}`. Klucze w słowniku muszą być niemutowalne, co oznacza, że mogą to być stringi, liczby lub krotki, ale nie listy czy inne słowniki. Wartości mogą być natomiast dowolnego typu, w tym także inne słowniki, co pozwala na tworzenie zagnieżdżonych struktur danych.

Dostęp do wartości w słowniku odbywa się przez podanie klucza w nawiasach kwadratowych `osoba["imie"]` lub za pomocą metody `get(klucz, domyslna)`, która w przypadku braku klucza zwraca wartość domyślną zamiast zgłaszania błędu `KeyError`. Iteracja po słowniku domyślnie przechodzi przez klucze, ale metody `keys()`, `values()` i `items()` pozwalają na iterację odpowiednio po kluczach, wartościach lub parach klucz-wartość. Słowniki są szczególnie przydatne przy przechowywaniu danych konfiguracyjnych, mapowaniu wartości i reprezentowaniu obiektów o znanej strukturze pól.

17Instrukcje warunkowe - if (decyzje w kodzie)

Co to jest instrukcja warunkowa?

Instrukcja if pozwala programowi podejmować decyzje. Kod wykonuje się tylko wtedy, gdy warunek jest spełniony (prawdziwy).

Struktura if

if warunek:
    # ten kod wykona się gdy warunek=True
else:
    # ten kod wykona się gdy warunek=False

Ważne zasady

  • Po if i else musi być dwukropek :
  • Kod do wykonania musi być wcięty (4 spacje)!
  • elif to skrót od "else if" - dodatkowy warunek
# Prosty przykład
wiek = 18

if wiek >= 18:
    print("Jesteś pełnoletni!")
    print("Możesz głosować.")
else:
    print("Nie jesteś jeszcze pełnoletni.")

# else if (elif)
wynik = 75

if wynik >= 90:
    print("Ocena: bardzo dobry (5)")
elif wynik >= 70:
    print("Ocena: dobry (4)")
elif wynik >= 50:
    print("Ocena: dostateczny (3)")
else:
    print("Ocena: niedostateczny (2)")

# Warunek złożony (and, or)
login = "admin"
haslo = "12345"

if login == "admin" and haslo == "12345":
    print("Zalogowany!")
elif login == "admin":
    print("Złe hasło!")
else:
    print("Nieznany użytkownik.")

Instrukcja warunkowa `if` w Pythonie pozwala na wykonywanie różnych bloków kodu w zależności od spełnienia określonych warunków. Podstawowa składnia to `if warunek:`, po którym następuje wcięty blok kodu wykonywany, gdy warunek jest prawdziwy. Słowo kluczowe `elif` (skrót od else if) pozwala na sprawdzenie kolejnych warunków, a `else` definiuje blok kodu wykonywany, gdy żaden z wcześniejszych warunków nie został spełniony. W Pythonie bloki kodu są wyznaczane przez wcięcia, a nie nawiasy klamrowe, co jest jedną z charakterystycznych cech języka.

Warunki w instrukcji `if` mogą być dowolnie złożone dzięki łączeniu operatorów porównania z operatorami logicznymi `and`, `or` i `not`. Python wspiera także składnię łańcuchową porównań, na przykład `if 18 <= wiek < 65:`, która jest czytelniejsza od odpowiednika z operatorem `and`. W Pythonie 3.10 wprowadzono instrukcję `match-case`, która stanowi odpowiednik switch-case znanego z innych języków i pozwala na eleganckie obsłużenie wielu wariantów wartości. Instrukcje warunkowe są fundamentem logiki decyzyjnej w każdym programie i pojawiają się w praktycznie każdym skrypcie Pythonowym.

18Pętla for - powtarzanie czynności

Co to jest pętla?

Pętla pozwala powtórzyć ten sam kod wiele razy. Zamiast pisać print("Hello") sto razy, używamy pętli.

Pętla for

for służy do przechodzenia przez elementy (np. listy):

for element in kolekcja:
    # wykonaj kod dla każdego elementu

Funkcja range()

range(n) generuje liczby od 0 do n-1:

  • range(5) - 0, 1, 2, 3, 4
  • range(1, 6) - 1, 2, 3, 4, 5
  • range(0, 10, 2) - 0, 2, 4, 6, 8
# Powtarzanie czynności
for i in range(5):
    print(f"Krok numer {i}")

# Iteracja po liście
owoce = ["jabłko", "banan", "wiśnia"]
for owoc in owoce:
    print(f"Lubię {owoc}")

# Enumerate - indeks i wartość
for index, owoc in enumerate(owoce):
    print(f"{index + 1}. {owoc}")

# range() z parametrami
print("Liczby parzyste od 0 do 10:")
for i in range(0, 11, 2):
    print(i, end=" ")

# break - wyjście z pętli
print("\nSzukam liczby 7:")
for i in range(10):
    if i == 7:
        print(f"Znalazłem! To liczba {i}")
        break
    print(f"Sprawdzam {i}...")

# continue - pomijanie iteracji
print("\nLiczby nieparzyste:")
for i in range(10):
    if i % 2 == 0:
        continue
    print(i, end=" ")

Pętla `for` w Pythonie jest używana do iteracji po elementach dowolnej sekwencji, takiej jak lista, string, krotka czy zakres liczb. Najczęstszym zastosowaniem jest przejście przez wszystkie elementy kolekcji za pomocą składni `for element in kolekcja:`, gdzie zmienna `element` przyjmuje kolejno wartości każdego elementu. Funkcja `range()` generuje sekwencje liczb całkowitych i jest często używana w pętlach `for`, gdy potrzebujemy licznika iteracji. Na przykład `range(5)` generuje liczby 0, 1, 2, 3, 4, a `range(2, 10, 3)` generuje 2, 5, 8.

Pętla `for` w Pythonie jest znacznie bezpieczniejsza niż w wielu innych językach, ponieważ automatycznie dostosowuje się do rozmiaru kolekcji i nie ma ryzyka wyjścia poza zakres. Wewnątrz pętli można używać instrukcji `break` do natychmiastowego przerwania pętli oraz `continue` do pominięcia bieżącej iteracji i przejścia do następnej. Python oferuje także klauzulę `else` dla pętli, która wykonuje się tylko wtedy, gdy pętla nie została przerwana przez `break`, co jest przydatne przy wyszukiwaniu elementów z warunkiem braku znalezienia.

19Pętla while - powtarzaj dopóki warunek

Pętla while

while powtarza kod dopóki warunek jest prawdziwy. Używamy jej, gdy nie wiemy z góry ile razy pętla ma się wykonać.

while warunek:
    # kod do powtórzenia

Przykłady zastosowań

  • Czekanie na określony stan systemu
  • Pobieranie danych od użytkownika dopóki nie poda poprawnych
  • Przetwarzanie elementów, których liczba nie jest znana

UWAGA!

Pamiętaj o zwiększaniu licznika! Inaczej powstanie pętla nieskończona - program zawiesi się!

# Prosty licznik
licznik = 0
while licznik < 5:
    print(f"Wartość: {licznik}")
    licznik += 1  # UWAGA! Zawsze zwiększaj!

print("Koniec pętli")

# Wczytywanie dopóki niepoprawne
haslo = ""
while haslo != "tajne":
    haslo = input("Podaj hasło: ")
    if haslo != "tajne":
        print("Złe hasło, spróbuj ponownie!")

print("Poprawne hasło! Zalogowany!")

# Symulacja odliczania
print("\nOdliczanie:")
i = 5
while i > 0:
    print(i)
    i -= 1
print("Start!")

# Pętla nieskończona (NIE RÓB TEGO!)
# while True:
#     print("Nieskończona pętla!")
# Aby przerwać: Ctrl + C

Pętla `while` w Pythonie wykonuje blok kodu tak długo, jak podany warunek pozostaje prawdziwy, co czyni ją idealną do sytuacji, gdy liczba iteracji nie jest znana z góry. Składnia `while warunek:` rozpoczyna pętlę, która sprawdza warunek przed każdym wykonaniem, a w przypadku fałszywego warunku od samego początku kod wewnątrz pętli nie wykona się ani razu. Pętla `while` jest często używana do implementacji interaktywnych menu, gier tekstowych oraz wszędzie tam, gdzie warunek zakończenia zależy od danych wejściowych użytkownika lub stanu systemu.

Szczególną ostrożność należy zachować przy pętlach `while`, aby uniknąć nieskończonych pętli, które mogą zablokować program. Zawsze należy upewnić się, że warunek pętli w końcu stanie się fałszywy przez modyfikację zmiennych wpływających na warunek wewnątrz pętli. Podobnie jak w przypadku pętli `for`, w pętli `while` można używać `break` i `continue` do sterowania przepływem, a także klauzuli `else`, która wykonuje się po naturalnym zakończeniu pętli. W Pythonie 3.8 wprowadzono operator przypisania `:=`, który pozwala na przypisanie wartości w wyrażeniu warunku pętli, co bywa przydatne przy przetwarzaniu strumieni danych.

20Sterowanie pętlami: pass, break, continue

Przerywanie pętli

  • break - natychmiast wychodzi z pętli
  • continue - przechodzi do następnej iteracji
  • pass - pusta instrukcja (nic nie robi)

break

Przydatne, gdy chcesz zakończyć wyszukiwanie po znalezieniu elementu lub wyjść z pętli po spełnieniu warunku.

continue

Przydatne, gdy chcesz pominąć pewne elementy (np. nie przetwarzać plików tymczasowych).

pass

Używane, gdy potrzebujesz "zająć miejsce" w kodzie, ale jeszcze nie masz kodu do wstawienia. Bez pass Python zgłosi błąd.

# BREAK - wyjście z pętli
print("Szukam pierwszej liczby większej od 100:")
for liczba in [10, 50, 90, 120, 150, 180]:
    if liczba > 100:
        print(f"Znalazłem: {liczba}")
        break
    print(f"Sprawdzam: {liczba}")

# CONTINUE - pomijanie elementów
print("\nWyświetlam tylko pliki (pomijam foldery):")
elementy = ["plik1.txt", "folder", "plik2.txt", "folder2", "plik3.txt"]
for element in elementy:
    if element.startswith("folder"):
        continue
    print(f"  - {element}")

# PASS - puste miejsce w kodzie
print("\nPrzykład z pass:")
for i in range(5):
    if i == 2:
        pass  # TODO: coś tu kiedyś będzie
    else:
        print(f"Przetwarzam element {i}")

# Praktyczny przykład: liczenie do napotkania błędu
print("\nPrzetwarzanie z limitem błędów:")
bledy = 0
for i in range(20):
    if bledy >= 3:
        print(f"Zbyt wiele błędów ({bledy}). Przerywam!")
        break
    print(f"Przetwarzanie {i}...")
    if i == 5 or i == 10:
        print(f"  Błąd przy {i}!")
        bledy += 1

Instrukcje sterujące `pass`, `break` i `continue` w Pythonie pozwalają na precyzyjne kontrolowanie przepływu wykonania w pętlach i warunkach. Instrukcja `pass` jest operacją pustą, która nie robi nic i służy jako placeholder w miejscach, gdzie składnia wymaga obecności bloku kodu, ale nie mamy jeszcze implementacji. Jest to szczególnie przydatne przy szkicowaniu struktury programu przed napisaniem właściwej logiki, ponieważ zapobiega błędom składniowym spowodowanym pustymi blokami.

Instrukcja `break` natychmiastowo przerywa wykonywanie bieżącej pętli i przekazuje sterowanie do pierwszej instrukcji poza pętlą, co jest używane przy wyszukiwaniu elementów spełniających warunek. Instrukcja `continue` pomija resztę kodu w bieżącej iteracji i przechodzi do następnej, co jest przydatne przy filtrowaniu elementów w pętli. Obie instrukcje działają zarówno w pętlach `for`, jak i `while`, a w przypadku pętli zagnieżdżonych wpływają tylko na najbardziej wewnętrzną pętlę, w której zostały użyte. W Pythonie nie ma instrukcji `goto`, a `break` i `continue` wraz z odpowiednio zaprojektowanymi warunkami w pełni pokrywają potrzeby związane ze sterowaniem przepływem pętli.

21Funkcje - wielokrotne użycie kodu

Co to jest funkcja?

Funkcja to nazwana grupa instrukcji, którą można wykonać wielokrotnie. Zamiast powtarzać ten sam kod, piszesz go raz i wywołujesz przez nazwę.

Tworzenie funkcji

def nazwa_funkcji():
    # kod funkcji

Wywoływanie funkcji

nazwa_funkcji()

Zalety funkcji

  • Unikanie powtarzania kodu
  • Łatwiejsze testowanie
  • Przejrzystszy kod
  • Możliwość ponownego użycia
# Definicja funkcji
def powitaj():
    """Funkcja wyświetlająca powitanie."""
    print("=" * 30)
    print("  Witaj w programie!")
    print("=" * 30)

# Wywołanie funkcji
powitaj()
powitaj()

# Funkcja z parametrem
def powitaj_osobe(imie):
    """Funkcja wita konkretną osobę."""
    print(f"Cześć, {imie}!")
    print("Miło Cię widzieć!")

powitaj_osobe("Anna")
powitaj_osobe("Bartek")

# Funkcja z wieloma parametrami
def dodaj(a, b):
    """Funkcja dodaje dwie liczby."""
    wynik = a + b
    print(f"{a} + {b} = {wynik}")

dodaj(5, 3)
dodaj(10, 20)

# Funkcja z parametrem domyślnym
def przywitaj(imie, czas_dnia="Dzień"):
    """Funkcja z pożegnaniem zależnym od pory dnia."""
    print(f"{czas_dnia}, {imie}!")

przywitaj("Anna")
przywitaj("Bartek", "Wieczór")
przywitaj("Celina", "Noc")

Funkcje w Pythonie są podstawowym mechanizmem organizacji kodu, pozwalającym na grupowanie powiązanych instrukcji pod jedną nazwą i wielokrotne ich wywoływanie. Definicja funkcji rozpoczyna się od słowa kluczowego `def`, po którym następuje nazwa funkcji, nawiasy z opcjonalnymi parametrami i dwukropek. Blok kodu funkcji jest wyznaczony przez wcięcie, a funkcja może zawierać instrukcję `return` do zwrócenia wartości, choć nie jest ona obowiązkowa. Funkcje w Pythonie są obiektami pierwszej klasy, co oznacza, że mogą być przypisywane do zmiennych, przekazywane jako argumenty do innych funkcji i zwracane z funkcji.

Parametry funkcji mogą mieć wartości domyślne, co czyni je opcjonalnymi podczas wywołania, na przykład `def powitanie(imie="Gościu"):`. Funkcje mogą przyjmować dowolną liczbę argumentów pozycyjnych za pomocą `*args` i nazwanych za pomocą `**kwargs`, co pozwala na tworzenie elastycznych interfejsów. W Pythonie funkcje mogą także zwracać wiele wartości jednocześnie jako krotkę, na przykład `return a, b`, co jest wygodne przy operacjach wymagających kilku wyników. Pisanie funkcji jest kluczową umiejętnością, ponieważ pozwala na unikanie powtarzania kodu, ułatwia testowanie i poprawia czytelność programu.

22Funkcje - zwracanie wartości (return)

Instrukcja return

return pozwala funkcji zwrócić wynik do kodu, który ją wywołał. Bez return funkcja zwraca None.

Przykłady

  • return wynik - zwraca pojedynczą wartość
  • return a, b - zwraca krotkę (tuple)
  • return (bez wartości) - kończy funkcję

Funkcje matematyczne

Funkcje idealnie nadają się do tworzenia kalkulatorów i przetwarzania danych.

# Funkcja zwracająca wartość
def dodaj(a, b):
    return a + b

def odejmij(a, b):
    return a - b

# Użycie wyniku
wynik = dodaj(10, 5)
print(f"Wynik dodawania: {wynik}")

# Łączenie funkcji
print(f"10 + 5 - 3 = {odejmij(dodaj(10, 5), 3)}")

# Funkcja zwracająca wiele wartości (krotka)
def dzielenie_z_reszta(a, b):
    calkowite = a // b
    reszta = a % b
    return calkowite, reszta

czesc, reszta = dzielenie_z_reszta(17, 5)
print(f"17 / 5 = {czesc} r. {reszta}")

# Funkcja bez return zwraca None
def brak_return():
    print("Coś robię, ale nic nie zwracam")

wynik = brak_return()
print(f"Wynik: {wynik}")

# Warunkowy return
def sprawdz_parzystosc(liczba):
    if liczba % 2 == 0:
        return True
    else:
        return False

print(f"4 parzyste? {sprawdz_parzystosc(4)}")
print(f"7 parzyste? {sprawdz_parzystosc(7)}")

Instrukcja `return` w Pythonie służy do zwracania wartości z funkcji i natychmiastowego zakończenia jej wykonania. Jeśli funkcja nie zawiera instrukcji `return` lub zawiera `return` bez wartości, zwraca specjalną wartość `None`, która oznacza brak wartości. Funkcja może zawierać wiele instrukcji `return` w różnych gałęziach kodu, co pozwala na wczesne zakończenie wykonania w zależności od spełnienia warunków. Jest to szczególnie przydatne przy walidacji danych wejściowych, gdzie nieprawidłowe dane powodują natychmiastowe zakończenie funkcji z odpowiednim komunikatem.

Zwracanie wielu wartości z funkcji realizuje się przez podanie ich po przecinku za `return`, a Python automatycznie pakuje je w krotkę. Po stronie wywołującej można rozpakować krotkę do osobnych zmiennych za pomocą składni `a, b = funkcja()`, co jest wygodne i czytelne. W Pythonie funkcje mogą zwracać wartości dowolnego typu, w tym inne funkcje, co jest podstawą programowania funkcyjnego i technik takich jak dekoratory. Zrozumienie mechanizmu zwracania wartości jest kluczowe dla budowania modularnych i testowalnych aplikacji w Pythonie.

23Zmienne lokalne i globalne

Zasięg zmiennych

Zmienna lokalna istnieje tylko wewnątrz funkcji, w której została stworzona.

Zmienna globalna istnieje w całym programie.

Jak to działa?

  • Zmienna stworzona wewnątrz funkcji = lokalna
  • Zmienna stworzona poza funkcjami = globalna

Słowo kluczowe global

Jeśli chcesz modyfikować zmienną globalną wewnątrz funkcji, użyj global nazwa.

# Zmienna globalna
moja_liczba = 10

def funkcja_zmieniajaca():
    """Ta funkcja nie zmienia zmiennej globalnej!"""
    moja_liczba = 20  # To jest zmienna LOKALNA!
    print(f"Wewnątrz funkcji: {moja_liczba}")  # 20

funkcja_zmieniajaca()
print(f"Na zewnątrz: {moja_liczba}")  # 10 (bez zmiany!)

# Aby faktycznie zmienić globalną:
def funkcja_poprawna():
    global moja_liczba
    moja_liczba = 20
    print(f"Wewnątrz funkcji: {moja_liczba}")

funkcja_poprawna()
print(f"Na zewnątrz: {moja_liczba}")  # 20

# Lepszy sposób - parametry i return
def dodaj_cztery(x):
    return x + 4

wynik = dodaj_cztery(moja_liczba)
print(f"Wynik: {wynik}")
print(f"Globalna nadal: {moja_liczba}")

Zmienne lokalne i globalne w Pythonie różnią się zakresem widoczności oraz czasem życia, co ma istotny wpływ na projektowanie funkcji i organizację kodu. Zmienna zadeklarowana wewnątrz funkcji jest zmienną lokalną, dostępną tylko w jej obrębie, i jest automatycznie usuwana z pamięci po zakończeniu funkcji. Zmienna zadeklarowana na najwyższym poziomie pliku, poza jakąkolwiek funkcją, jest zmienną globalną i może być odczytywana z dowolnego miejsca w module. Domyślnie przypisanie wartości do zmiennej wewnątrz funkcji tworzy zmienną lokalną, nawet jeśli istnieje zmienna globalna o tej samej nazwie.

Aby modyfikować zmienną globalną wewnątrz funkcji, należy użyć słowa kluczowego `global`, które informuje interpreter, że odwołujemy się do zmiennej z zakresu globalnego. Nadużywanie zmiennych globalnych jest uważane za złą praktykę, ponieważ utrudnia śledzenie przepływu danych i może prowadzić do trudnych do zdiagnozowania błędów. W dobrze zaprojektowanych programach dane są przekazywane między funkcjami przez parametry i wartości zwracane, a zmienne globalne są używane oszczędnie, głównie do przechowywania stałych konfiguracyjnych lub stanu na poziomie aplikacji.

24Elastyczne argumenty funkcji (*args, **kwargs)

*args - dowolna liczba argumentów

Gdy chcesz, aby funkcja przyjmowała dowolną liczbę argumentów pozycyjnych, użyj *args. Wewnątrz funkcji masz dostęp do krotki (tuple) argumentów.

**kwargs - dowolna liczba argumentów nazwanych

Gdy chcesz, aby funkcja przyjmowała dowolną liczbę argumentów z nazwami, użyj **kwargs. Wewnątrz funkcji masz dostęp do słownika argumentów.

Przykłady zastosowań

  • Funkcja print() używa *args
  • Funkcje formatujące często używają **kwargs
# *args - dowolna liczba argumentów pozycyjnych
def sumuj_wszystko(*liczby):
    """Sumuje dowolną liczbę liczb."""
    suma = 0
    for liczba in liczby:
        suma += liczba
    return suma

print(sumuj_wszystko(1, 2, 3))
print(sumuj_wszystko(10, 20, 30, 40))
print(sumuj_wszystko(5))

# **kwargs - dowolna liczba argumentów nazwanych
def drukuj_dane(**informacje):
    """Wyświetla dane osobowe."""
    for klucz, wartosc in informacje.items():
        print(f"{klucz}: {wartosc}")

drukuj_dane(imie="Anna", wiek=20, miasto="Warszawa")

# Połączenie *args i **kwargs
def kompletna_funkcja(*args, **kwargs):
    print(f"Argumenty pozycyjne: {args}")
    print(f"Argumenty nazwane: {kwargs}")

kompletna_funkcja(1, 2, 3, imie="Test", status="OK")

# Praktyczny przykład
def srednia(*oceny):
    if not oceny:
        return 0
    return sum(oceny) / len(oceny)

print(f"Średnia: {srednia(5, 4, 3, 5)}")

Python oferuje elastyczne mechanizmy przekazywania argumentów do funkcji, w tym `*args` do obsługi dowolnej liczby argumentów pozycyjnych i `**kwargs` do argumentów nazwanych. Parametr `*args` zbiera wszystkie dodatkowe argumenty pozycyjne w krotkę, co pozwala na napisanie funkcji przyjmującej zmienną liczbę argumentów bez konieczności definiowania każdego z osobna. Parametr `**kwargs` zbiera dodatkowe argumenty nazwane w słownik, gdzie kluczami są nazwy parametrów, a wartościami przekazane wartości. Te mechanizmy są szeroko stosowane w bibliotekach Pythona, szczególnie przy dekoratorach i funkcjach fabrykujących.

Rozpakowywanie argumentów to technika odwrotna, która pozwala na przekazanie elementów listy lub słownika jako osobnych argumentów funkcji za pomocą operatorów `*` i `**`. Na przykład `funkcja(*lista)` rozpakowuje elementy listy na osobne argumenty pozycyjne, a `funkcja(**slownik)` rozpakowuje pary klucz-wartość na argumenty nazwane. Te techniki są niezwykle przydatne przy delegowaniu wywołań do innych funkcji, w metaprogramowaniu oraz przy pracy z funkcjami o zmiennej liczbie parametrów. Zrozumienie `*args` i `**kwargs` jest ważne przy czytaniu kodu bibliotek i frameworków Pythona.

25Obsługa plików - odczyt danych

Otwieranie pliku

Do otwierania plików służy funkcja open():

plik = open("nazwa.txt", "r")

Tryby otwarcia

  • "r" - odczyt (domyślny)
  • "w" - zapis (nadpisuje)
  • "a" - dopisywanie
  • "b" - modyfikator binarny (łącz z "r", "w" lub "a", np. "rb")

WAŻNE!

Zawsze zamykaj pliki! Używaj with - automatycznie zamyka plik nawet przy błędach.

# Odczyt całego pliku
with open("plik.txt", "r", encoding="utf-8") as plik:
    tresc = plik.read()
    print(tresc)

# Odczyt linia po linii
with open("plik.txt", "r", encoding="utf-8") as plik:
    for linia in plik:
        print(linia.strip())

# Odczyt wszystkich linii do listy
with open("plik.txt", "r", encoding="utf-8") as plik:
    linie = plik.readlines()
    for i, linia in enumerate(linie):
        print(f"{i+1}: {linia.strip()}")

# Bezpieczne otwieranie (starsza metoda)
try:
    plik = open("dane.txt", "r")
    dane = plik.read()
    print(dane)
finally:
    plik.close()

# Sprawdzanie czy plik istnieje
import os
if os.path.exists("plik.txt"):
    print("Plik istnieje!")
else:
    print("Plik nie istnieje.")

Obsługa plików w Pythonie jest realizowana za pomocą wbudowanej funkcji `open()`, która otwiera plik i zwraca obiekt pliku umożliwiający odczyt lub zapis danych. Podstawowe tryby otwarcia pliku to `'r'` do odczytu, `'w'` do zapisu (nadpisanie istniejącego pliku), `'a'` do dopisywania na końcu pliku oraz `'r+'` do odczytu i zapisu. Najbezpieczniejszym sposobem pracy z plikami jest użycie menedżera kontekstu `with`, który automatycznie zamyka plik po zakończeniu bloku kodu, nawet w przypadku wystąpienia błędu. Odczyt całej zawartości pliku do stringa wykonuje metoda `read()`, a odczyt linia po linii umożliwia pętla `for line in plik:`.

Zapis danych do pliku odbywa się za pomocą metody `write()`, która przyjmuje string jako argument. W przypadku pracy z kodowaniem znaków innym niż domyślne, należy podać parametr `encoding`, na przykład `open("plik.txt", "w", encoding="utf-8")`. Python oferuje także moduły do pracy z różnymi formatami plików, takie jak `csv` do plików CSV, `json` do formatu JSON czy `pickle` do serializacji obiektów. Przy pracy z dużymi plikami warto przetwarzać je strumieniowo, linia po linii, zamiast wczytywać całą zawartość do pamięci, co mogłoby prowadzić do przekroczenia dostępnego RAM-u.

26Obsługa plików - zapis danych

Zapisywanie do pliku

Do zapisu używamy trybu "w" (write) lub "a" (append):

  • "w" - tworzy nowy plik lub nadpisuje istniejący
  • "a" - dopisuje na końcu istniejącego pliku

Metody zapisu

  • plik.write(tekst) - zapisuje tekst
  • plik.writelines(lista) - zapisuje wiele linii

Porada

Dodawaj \n na końcu każdej linii! Python nie dodaje ich automatycznie.

# Zapis do nowego pliku (tryb "w")
with open("nowy_plik.txt", "w", encoding="utf-8") as plik:
    plik.write("Pierwsza linia\n")
    plik.write("Druga linia\n")
    plik.write("Trzecia linia\n")

print("Plik został zapisany!")

# Dopisywanie do istniejącego pliku (tryb "a")
with open("nowy_plik.txt", "a", encoding="utf-8") as plik:
    plik.write("Czwarta linia (dopisana)\n")
    plik.write("Piąta linia (dopisana)\n")

print("Dopisano do pliku!")

# Zapis listy linii
linie_do_zapisu = ["Linia 1\n", "Linia 2\n", "Linia 3\n"]
with open("lista.txt", "w", encoding="utf-8") as plik:
    plik.writelines(linie_do_zapisu)

# Praktyczny przykład: zapis danych użytkownika
imie = "Anna"
wiek = 20
miasto = "Warszawa"

with open("uzytkownik.txt", "w", encoding="utf-8") as plik:
    plik.write(f"Imię: {imie}\n")
    plik.write(f"Wiek: {wiek}\n")
    plik.write(f"Miasto: {miasto}\n")

print("Dane zapisane!")

Zapis danych do plików w Pythonie jest równie prosty jak odczyt i opiera się na tych samych funkcjach z trybem `'w'` (write) do tworzenia nowego pliku lub nadpisywania istniejącego oraz `'a'` (append) do dopisywania danych na końcu istniejącego pliku. Przy użyciu trybu `'w'` należy zachować ostrożność, ponieważ każda operacja otwarcia pliku w tym trybie usuwa jego poprzednią zawartość. Do zapisu wielu linii na raz można użyć metody `writelines()`, która przyjmuje listę stringów i zapisuje je bez dodawania znaków nowej linii między nimi.

W praktyce administracyjnej zapis do plików jest często używany do logowania zdarzeń, generowania raportów i zapisywania wyników przetwarzania danych. Dobrą praktyką jest jawne określanie kodowania pliku podczas zapisu, aby uniknąć problemów z polskimi znakami diakrytycznymi i innymi znakami spoza ASCII. Menedżer kontekstu `with` zapewnia automatyczne zamknięcie pliku nawet przy wystąpieniu błędu, co zapobiega utracie danych i blokowaniu plików przez system operacyjny. W przypadku zapisu dużych ilości danych warto rozważyć buforowanie i okresowe opróżnianie bufora metodą `flush()`.

27Moduł os - zarządzanie plikami i katalogami

Moduł os

os to wbudowany moduł do komunikacji z systemem operacyjnym. Pozwala na:

  • Tworzenie i usuwanie katalogów
  • Sprawdzanie istnienia plików
  • Zmiana nazwy i usuwanie plików
  • Pobieranie aktualnej ścieżki

Przydatne funkcje os

  • os.getcwd() - aktualny katalog
  • os.listdir() - lista plików w katalogu
  • os.mkdir() - tworzenie katalogu
  • os.remove() - usuwanie pliku
  • os.path.exists() - sprawdzenie czy istnieje
import os

# Aktualny katalog roboczy
katalog = os.getcwd()
print(f"Aktualny katalog: {katalog}")

# Lista plików w katalogu
pliki = os.listdir(".")
print(f"Pliki: {pliki}")

# Sprawdzenie czy plik/katalog istnieje
if os.path.exists("plik.txt"):
    print("Plik istnieje!")
else:
    print("Plik nie istnieje")

# Tworzenie katalogu
if not os.path.exists("nowy_katalog"):
    os.mkdir("nowy_katalog")
    print("Katalog utworzony!")

# Zmiana nazwy pliku
if os.path.exists("stary.txt"):
    os.rename("stary.txt", "nowy.txt")
    print("Zmieniono nazwę!")

# Usuwanie pliku
if os.path.exists("do_usuniecia.txt"):
    os.remove("do_usuniecia.txt")
    print("Plik usunięty!")

# Ścieżki - łączenie
sciezka = os.path.join("katalog", "podkatalog", "plik.txt")
print(f"Pełna ścieżka: {sciezka}")

Moduł `os` w Pythonie jest podstawowym narzędziem do interakcji z systemem operacyjnym, oferującym funkcje do zarządzania plikami i katalogami. Funkcja `os.listdir()` zwraca listę wszystkich plików i katalogów w podanej ścieżce, `os.mkdir()` tworzy nowy katalog, a `os.makedirs()` tworzy całą strukturę katalogów, włącznie z katalogami pośrednimi. Do usuwania plików służy `os.remove()`, a do usuwania pustych katalogów `os.rmdir()`. Funkcja `os.rename()` pozwala na zmianę nazwy lub przeniesienie pliku, co jest przydatne przy organizacji danych.

Moduł `os` oferuje także funkcje do sprawdzania właściwości plików, takie jak `os.path.exists()` do sprawdzania istnienia ścieżki, `os.path.isfile()` i `os.path.isdir()` do określania typu elementu. Funkcja `os.path.join()` łączy składniki ścieżki w sposób niezależny od systemu operacyjnego, automatycznie używając odpowiedniego separatora. Do odczytu i ustawiania zmiennych środowiskowych służą `os.environ.get()` i `os.environ[]`. W nowszych wersjach Pythona zaleca się używanie modułu `pathlib` zamiast `os.path`, ponieważ oferuje on bardziej nowoczesne i spójne API do pracy ze ścieżkami.

28Moduł pathlib - nowoczesna praca ze ścieżkami

Pathlib - obiektowe podejście

pathlib to nowoczesny moduł (Python 3.4+) do pracy ze ścieżkami plików. Jest bardziej czytelny niż os.path.

Zalety pathlib

  • Łatwiejsze łączenie ścieżek (operator /)
  • Prostszy dostęp do właściwości pliku
  • Działa na wszystkich systemach
  • Intuicyjna składnia

Tworzenie ścieżki

Path("katalog") / "plik.txt"

from pathlib import Path

# Tworzenie obiektu Path
sciezka = Path(".")
print(sciezka)

# Łączenie ścieżek (elegancko!)
plik = Path("dokumenty") / "notatki" / "notatka.txt"
print(plik)

# Właściwości ścieżki
print(f"Katalog: {plik.parent}")
print(f"Nazwa: {plik.name}")
print(f"Rozszerzenie: {plik.suffix}")

# Sprawdzanie istnienia
if plik.exists():
    print(f"Plik istnieje: {plik}")
else:
    print("Plik nie istnieje")

# Tworzenie katalogu
katalog = Path("nowy_katalog")
katalog.mkdir(exist_ok=True)

# Tworzenie pliku
plik.write_text("Zawartość pliku", encoding="utf-8")

# Odczyt pliku
zawartosc = plik.read_text(encoding="utf-8")
print(zawartosc)

# Usuwanie
plik.unlink()
katalog.rmdir()

# Lista plików z filtrem
for p in Path(".").glob("*.txt"):
    print(f"Plik txt: {p}")

Moduł `pathlib` wprowadzony w Pythonie 3.4 stanowi nowoczesną alternatywę dla tradycyjnego modułu `os.path`, oferując obiektowe podejście do reprezentacji ścieżek plików. Główną zaletą `pathlib` jest klasa `Path`, której instancje reprezentują ścieżki i udostępniają metody do wykonywania operacji na plikach i katalogach bez konieczności używania osobnych funkcji. Na przykład `Path("/tmp") / "plik.txt"` tworzy nową ścieżkę przez użycie operatora dzielenia, co jest bardziej czytelne niż `os.path.join("/tmp", "plik.txt")`. Metody takie jak `exists()`, `is_file()`, `is_dir()`, `mkdir()`, `rename()` i `unlink()` są dostępne bezpośrednio na obiekcie Path.

Pathlib oferuje także wygodne metody do iteracji po zawartości katalogów: `iterdir()` zwraca generator wszystkich elementów, `glob()` wyszukuje pliki według wzorca z symbolami wieloznacznymi, a `rglob()` robi to samo rekurencyjnie we wszystkich podkatalogach. Odczytywanie i zapisywanie plików jest możliwe przez metody `read_text()` i `write_text()`, które automatycznie obsługują kodowanie. Ze względu na czytelność i spójność API, moduł `pathlib` jest obecnie zalecanym sposobem pracy ze ścieżkami w Pythonie, szczególnie w nowych projektach, gdzie nie ma konieczności zachowania kompatybilności wstecznej.

29Obsługa błędów - try i except

Co to są wyjątki?

Wyjątek to błąd, który występuje podczas działania programu (np. dzielenie przez zero, brak pliku).

Obsługa wyjątków

Pozwala "złapać" błąd i zareagować na niego, zamiast całkowicie zatrzymać program:

try:
    # kod który może spowodować błąd
except:
    # co zrobić gdy wystąpi błąd

Rodzaje błędów

  • FileNotFoundError - plik nie istnieje
  • ZeroDivisionError - dzielenie przez zero
  • ValueError - zła wartość
  • TypeError - zły typ
# Próba odczytu nieistniejącego pliku
try:
    with open("nie_istnieje.txt", "r") as plik:
        print(plik.read())
except FileNotFoundError:
    print("Błąd: Plik nie istnieje!")

# Obsługa wielu typów błędów
try:
    liczba = int(input("Podaj liczbę: "))
    wynik = 10 / liczba
    print(f"Wynik: {wynik}")
except ValueError:
    print("Błąd: To nie jest liczba!")
except ZeroDivisionError:
    print("Błąd: Nie można dzielić przez zero!")
except Exception as e:
    print(f"Nieoczekiwany błąd: {e}")

# Przechwytywanie błędu z zmienną
try:
    wynik = 10 / 0
except ZeroDivisionError as blad:
    print(f"Złapano błąd: {blad}")
    print(f"Typ błędu: {type(blad)}")

# Blok else - wykonuje się gdy nie ma błędu
try:
    x = int("123")
except ValueError:
    print("Błąd konwersji!")
else:
    print(f"Udało się! x = {x}")

Obsługa błędów w Pythonie za pomocą bloków `try` i `except` jest fundamentalnym mechanizmem pozwalającym na kontrolowane reagowanie na wyjątki bez przerywania działania programu. Blok `try` zawiera kod, który może potencjalnie zgłosić wyjątek, a blok `except` definiuje kod obsługi błędu, który zostanie wykonany tylko w przypadku wystąpienia wyjątku. Można określić konkretny typ wyjątku do przechwycenia, na przykład `except ValueError:`, co pozwala na różne strategie obsługi dla różnych rodzajów błędów. Przechwytywanie wszystkich wyjątków za pomocą gołego `except:` jest odradzane, ponieważ może ukryć poważne błędy programistyczne.

Blok `try` może zawierać wiele bloków `except` dla różnych typów wyjątków, a także opcjonalny blok `else`, który wykonuje się tylko wtedy, gdy nie wystąpił żaden wyjątek. Python oferuje bogatą hierarchię wbudowanych typów wyjątków, od ogólnego `Exception` po wyspecjalizowane, takie jak `FileNotFoundError`, `PermissionError` czy `KeyError`. Znajomość typów wyjątków i umiejętność ich precyzyjnego przechwytywania jest kluczowa dla pisania niezawodnych programów, które potrafią elegancko radzić sobie z sytuacjami awaryjnymi, takimi jak brak pliku, brak uprawnień czy nieprawidłowe dane wejściowe.

30Obsługa błędów - finally i raise

Blok finally

Kod w bloku finally wykonuje się zawsze, niezależnie od tego, czy był błąd czy nie. Używamy go do "sprzątania" - zamykania plików, połączeń.

Słowo kluczowe raise

raise pozwala celowo wywołać błąd (wyjątek). Używamy go, gdy chcemy poinformować o błędnej sytuacji.

Przykłady

  • Zamykanie połączenia z bazą danych
  • Zamykanie pliku nawet przy błędzie
  • Walidacja danych wejściowych
# finally - wykonuje się zawsze
print("Przykład z finally:")
try:
    plik = open("testowy.txt", "r")
    print(plik.read())
    x = 1 / 0
except FileNotFoundError:
    print("Plik nie istnieje!")
except ZeroDivisionError:
    print("Dzielenie przez zero!")
finally:
    print("To wykonuje się ZAWSZE!")
    if 'plik' in locals():
        plik.close()
    print("Zasoby zwolnione.")

# raise - rzucanie własnych błędów
def sprawdz_wiek(wiek):
    """Funkcja sprawdza czy wiek jest poprawny."""
    if wiek < 0:
        raise ValueError("Wiek nie może być ujemny!")
    if wiek > 150:
        raise ValueError("Wiek jest niemożliwie duży!")
    return True

try:
    sprawdz_wiek(-5)
except ValueError as e:
    print(f"Błąd walidacji: {e}")

# Łączenie raise i except
def podziel(a, b):
    if b == 0:
        raise ZeroDivisionError("Nie można dzielić przez zero!")
    return a / b

try:
    wynik = podziel(10, 0)
except ZeroDivisionError as e:
    print(f"Błąd: {e}")

Blok `finally` w Pythonie jest wykonywany zawsze, niezależnie od tego, czy w bloku `try` wystąpił wyjątek, czy nie, co czyni go idealnym miejscem do umieszczania kodu porządkującego, takiego jak zamykanie plików, zwalnianie zasobów czy przywracanie stanu początkowego. Nawet jeśli w bloku `try` wystąpi `return`, `break` lub `continue`, blok `finally` zostanie wykonany przed faktycznym zakończeniem funkcji lub pętli. Dla typowych operacji na plikach zaleca się jednak używanie menedżera kontekstu `with`, który automatycznie zapewnia podobne gwarancje.

Instrukcja `raise` pozwala na ręczne zgłoszenie wyjątku w dowolnym miejscu kodu, co jest przydatne przy walidacji danych wejściowych lub sygnalizowaniu błędów logicznych. Można zgłosić zarówno wbudowany typ wyjątku, jak i własny, zdefiniowany przez użytkownika przez dziedziczenie po klasie `Exception`. `raise` może być także użyte bez argumentu wewnątrz bloku `except` do ponownego zgłoszenia tego samego wyjątku po częściowym przetworzeniu błędu. Tworzenie własnych typów wyjątków jest dobrą praktyką w większych projektach, ponieważ pozwala na precyzyjne określenie, jakie błędy może zgłaszać dana funkcja lub moduł.

31Argumenty wiersza poleceń - sys.argv

Co to są argumenty wiersza poleceń?

To informacje przekazywane do skryptu przy jego uruchomieniu:

python skrypt.py arg1 arg2 arg3

Moduł sys

sys.argv to lista argumentów:

  • sys.argv[0] - nazwa skryptu
  • sys.argv[1] - pierwszy argument
  • sys.argv[2] - drugi argument
  • itd.

Przydatne przy automatyzacji

Pozwala tworzyć elastyczne skrypty, które zachowują się inaczej w zależności od podanych argumentów.

# Zapisz jako: args_demo.py i uruchom:
# python args_demo.py plik.txt opcja1 opcja2

import sys

print(f"Nazwa skryptu: {sys.argv[0]}")
print(f"Liczba argumentów: {len(sys.argv)}")

# Wszystkie argumenty
print(f"Wszystkie argumenty: {sys.argv}")

# Iteracja po argumentach
for i, arg in enumerate(sys.argv):
    print(f"Argument {i}: {arg}")

# Bezpieczne pobieranie argumentów
if len(sys.argv) > 1:
    pierwszy_arg = sys.argv[1]
else:
    pierwszy_arg = "brak"

print(f"\nPierwszy argument: {pierwszy_arg}")

# Przykład praktyczny
# python args_demo.py copy plik_zrodlowy plik_docelowy
if len(sys.argv) >= 4:
    operacja = sys.argv[1]
    zrodlo = sys.argv[2]
    cel = sys.argv[3]
    print(f"{operacja}: {zrodlo} -> {cel}")
else:
    print("Użycie: python skrypt.py  <źródło> ")

Argumenty wiersza poleceń w Pythonie są dostępne za pomocą listy `sys.argv`, która jest automatycznie wypełniana przez interpreter przy uruchomieniu skryptu. Pierwszym elementem `sys.argv[0]` jest zawsze nazwa pliku skryptu, a kolejne elementy to argumenty przekazane przez użytkownika w terminalu. Na przykład uruchomienie `python skrypt.py --help` spowoduje, że `sys.argv[1]` będzie zawierać string `"--help"`. Moduł `sys` musi być zaimportowany przed użyciem `sys.argv`, a liczbę przekazanych argumentów można sprawdzić za pomocą `len(sys.argv)`.

Przetwarzanie argumentów za pomocą `sys.argv` jest proste i wystarczające do prostych skryptów, ale przy bardziej złożonych interfejsach wiersza poleceń warto skorzystać z modułu `argparse`, który oferuje zaawansowane możliwości. W przypadku braku argumentów lub podania nieprawidłowej liczby warto wyświetlić użytkownikowi informację o poprawnym sposobie użycia skryptu. Lista `sys.argv` zawiera zawsze stringi, dlatego argumenty liczbowe wymagają jawnej konwersji za pomocą `int()` lub `float()` z odpowiednią obsługą błędów w przypadku nieprawidłowego formatu danych.

32Krotki (tuples) - niezmienne listy

Co to jest krotka?

Krotka (tuple) to jak lista, ale niemutowalna - po utworzeniu nie można jej zmieniać. Definiujemy ją używając nawiasów okrągłych ().

Tworzenie krotki

krotka = (1, 2, 3)

Różnice między listą a krotką

  • Lista - [1, 2, 3] - można zmieniać
  • Krotka - (1, 2, 3) - nie można zmieniać

Kiedy używać?

  • Gdy dane nie powinny się zmieniać (np. współrzędne GPS)
  • Krotki są szybsze od list
  • Mogą być używane jako klucze słowników
# Tworzenie krotek
wspolrzedne = (50.0, 19.9)  # szerokość, długość
kolor_rgb = (255, 128, 0)   # wartości RGB

# Dostęp do elementów
print(wspolrzedne[0])   # 50.0
print(wspolrzedne[1])   # 19.9
print(kolor_rgb[-1])    # 0

# Iteracja
for wartosc in kolor_rgb:
    print(wartosc)

# Rozpakowanie (unpacking)
x, y = wspolrzedne
print(f"X = {x}, Y = {y}")

# Konwersja lista <-> krotka
lista = [1, 2, 3]
krotka = tuple(lista)
nowa_lista = list(krotka)

# Krotka jednoelementowa (wymaga przecinka!)
jednoelementowa = (42,)  # WAŻNE: przecinek!
print(jednoelementowa)

# Funkcje zwracające krotki
def dzielenie(a, b):
    return a // b, a % b

wynik, reszta = dzielenie(17, 5)
print(f"17 / 5 = {wynik} r. {reszta}")

Krotki w Pythonie są strukturami danych bardzo podobnymi do list, ale z kluczową różniącą je cechą: są niemutowalne, co oznacza, że po utworzeniu nie można zmienić ich zawartości. Krotkę tworzy się za pomocą nawiasów okrągłych, na przykład `krotka = (1, 2, 3)`, choć w wielu kontekstach nawiasy są opcjonalne i można użyć samego przecinka, na przykład `krotka = 1, 2, 3`. Niemutowalność krotek sprawia, że są one bezpieczniejsze do przechowywania danych, które nie powinny ulec zmianie, oraz mogą być używane jako klucze w słownikach, w przeciwieństwie do list.

Krotki są często używane do grupowania powiązanych ze sobą wartości, takich jak współrzędne punktu `(x, y)`, rekordy z bazy danych czy wartości zwracane z funkcji. Rozpakowywanie krotek pozwala na przypisanie ich elementów do osobnych zmiennych w jednej linii, na przykład `imie, wiek = ("Anna", 25)`. Mimo swojej prostoty krotki są niezwykle przydatne w codziennym programowaniu, szczególnie w połączeniu z funkcjami zwracającymi wiele wartości oraz przy iteracji po parach klucz-wartość w słownikach za pomocą metody `items()`.

33Zbiory (sets) - unikalne elementy

Co to jest zbiór?

Zbiór (set) to kolekcja unikalnych elementów bez powtórzeń. Definiujemy go używając nawiasów klamrowych {}.

Właściwości zbiorów

  • Nie ma powtórzeń - każdy element występuje raz
  • Kolejność nie jest gwarantowana
  • Brak indeksowania - nie ma zbior[0]

Operacje na zbiorach

  • add() - dodaj element
  • remove() - usuń element
  • union() - suma zbiorów
  • intersection() - iloczyn zbiorów
  • difference() - różnica zbiorów
# Tworzenie zbioru
owoce = {"jabłko", "banan", "wiśnia", "jabłko"}
print(f"Owoce: {owoce}")  # jabłko tylko raz!

# Sprawdzanie przynależności
print("banan" in owoce)   # True
print("gruszka" in owoce) # False

# Dodawanie i usuwanie
owoce.add("gruszka")
print(f"Po dodaniu: {owoce}")
owoce.remove("banan")
print(f"Po usunięciu: {owoce}")
owoce.discard("winogrono")  # nie rzuca błędu

# Operacje na zbiorach
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

print(f"Suma: {a | b}")
print(f"Iloczyn: {a & b}")
print(f"Różnica a-b: {a - b}")
print(f"Różnica b-a: {b - a}")

# Usuwanie duplikatów z listy
lista = [1, 2, 3, 2, 1, 4, 3, 5]
unikalne = list(set(lista))
print(f"Bez duplikatów: {unikalne}")

Zbiory w Pythonie są strukturami danych przechowującymi nieuporządkowane kolekcje unikalnych elementów, co czyni je idealnymi do operacji związanych z eliminacją duplikatów i testowaniem przynależności. Zbiór tworzy się za pomocą nawiasów klamrowych, na przykład `zbiór = {1, 2, 3, 4, 5}`, lub za pomocą funkcji `set()` z listy. Główną cechą zbiorów jest to, że każdy element może występować tylko raz, co automatycznie usuwa duplikaty przy konwersji z listy. Zbiory są implementowane jako tablice mieszające, co zapewnia bardzo szybkie operacje sprawdzania przynależności za pomocą operatora `in`.

Zbiory oferują bogaty zestaw operacji matematycznych na kolekcjach, takich jak suma `|`, przecięcie `&`, różnica `-` i różnica symetryczna `^`. Na przykład `{1, 2, 3} & {2, 3, 4}` zwraca `{2, 3}`, czyli elementy wspólne obu zbiorów. Metody takie jak `add()`, `remove()`, `discard()` i `pop()` umożliwiają modyfikację zbioru, a `issubset()` i `issuperset()` sprawdzają relacje między zbiorami. Zbiory są szczególnie przydatne przy deduplikacji danych, testowaniu przynależności oraz wszędzie tam, gdzie potrzebne są szybkie operacje na kolekcjach unikalnych elementów.

34List comprehensions - zwięzłe tworzenie list

Co to jest?

List comprehension to zwięzły sposób tworzenia list. Pozwala zastąpić pętlę for jedną linijką kodu.

Składnia

[wyrażenie for element in kolekcja]

lub z warunkiem:

[wyrażenie for element in kolekcja if warunek]

Przykłady zastosowań

  • Tworzenie listy kwadratów liczb
  • Filtrowanie elementów listy
  • Transformacja danych
# Tradycyjna pętla
kwadraty = []
for i in range(10):
    kwadraty.append(i ** 2)
print(f"Kwadraty: {kwadraty}")

# List comprehension - krótsza wersja
kwadraty = [i ** 2 for i in range(10)]
print(f"Kwadraty: {kwadraty}")

# Z warunkiem - tylko parzyste
parzyste = [i for i in range(20) if i % 2 == 0]
print(f"Parzyste: {parzyste}")

# Z transformacją - wielkie litery
imiona = ["anna", "bartek", "celina"]
wielkie = [imie.upper() for imie in imiona]
print(f"Wielkie: {wielkie}")

# Filtrowanie i transformacja
liczby = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
wynik = [x * 2 for x in liczby if x % 2 == 0]
print(f"Parzyste x2: {wynik}")

# Zagnieżdżone comprehension
macierz = [[i*j for j in range(3)] for i in range(3)]
print(f"Macierz 3x3: {macierz}")

# Wyciąganie rozszerzeń plików
pliki = ["dokument.docx", "obrazek.jpg", "tekst.txt", "foto.png"]
rozszerzenia = [p.split(".")[-1] for p in pliki]
print(f"Rozszerzenia: {set(rozszerzenia)}")

List comprehensions w Pythonie to zwięzła i wydajna składnia do tworzenia nowych list poprzez przekształcenie istniejącej kolekcji, często z użyciem warunku filtrującego. Podstawowa składnia `[wyrażenie for element in kolekcja if warunek]` pozwala na zapisanie w jednej linii kodu operacji, która w tradycyjnym podejściu wymagałaby kilku linii z pętlą i warunkiem. Na przykład `[x**2 for x in range(10) if x % 2 == 0]` tworzy listę kwadratów liczb parzystych od 0 do 9, co jest zarówno czytelne, jak i efektywne.

List comprehensions mogą być zagnieżdżane, co pozwala na tworzenie list wielowymiarowych i iloczynów kartezjańskich, na przykład `[(x, y) for x in range(3) for y in range(3)]` tworzy listę wszystkich par współrzędnych. Python oferuje także podobną składnię dla innych typów kolekcji: `{}` dla set comprehensions, `{k: v for ...}` dla dict comprehensions oraz `()` dla generator comprehensions, które tworzą leniwie ewaluowane generatory. List comprehensions są preferowanym sposobem tworzenia list w Pythonie ze względu na czytelność i wydajność, ale przy bardzo złożonych przekształceniach warto rozważyć tradycyjną pętlę dla lepszej czytelności.

35Moduł datetime - praca z datą i czasem

Moduł datetime

datetime to wbudowany moduł do pracy z datami i czasem. Niezbędny do:

  • Logowania działań
  • Tworzenia nazwy plików z datą
  • Obliczeń czasowych

Tworzenie obiektu datetime

  • datetime.now() - aktualna data i czas
  • datetime(2025, 1, 15) - konkretna data

Formatowanie

strftime(format) - konwertuje datetime na string:

  • %Y - rok (2025)
  • %m - miesiąc (01)
  • %d - dzień (15)
  • %H:%M:%S - godzina:minuta:sekunda
import datetime

# Aktualna data i czas
teraz = datetime.datetime.now()
print(f"Teraz: {teraz}")

# Samo data
dzis = datetime.date.today()
print(f"Dziś: {dzis}")

# Formatowanie daty
print(teraz.strftime("%Y-%m-%d"))
print(teraz.strftime("%d.%m.%Y"))
print(teraz.strftime("%H:%M:%S"))
print(teraz.strftime("%Y-%m-%d %H:%M"))

# Tworzenie konkretnej daty
data_urodzin = datetime.date(2000, 5, 20)
print(f"Data urodzin: {data_urodzin}")

# Operacje na datach (timedelta)
jutro = teraz + datetime.timedelta(days=1)
tydzien_temu = teraz - datetime.timedelta(weeks=1)
za_godzinę = teraz + datetime.timedelta(hours=1)

print(f"Jutro: {jutro.strftime('%Y-%m-%d')}")

# Różnica między datami
d1 = datetime.date(2025, 1, 1)
d2 = datetime.date(2025, 1, 15)
roznica = d2 - d1
print(f"Różnica dni: {roznica.days}")

# Nazwa pliku z datą
def nazwa_pliku_z_data(prefix):
    data = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    return f"{prefix}_{data}.txt"

print(nazwa_pliku_z_data("log"))

Moduł `datetime` w Pythonie dostarcza zaawansowanych narzędzi do pracy z datami, czasem i przedziałami czasowymi. Główne klasy modułu to `datetime` reprezentująca konkretny moment w czasie, `date` dla dat, `time` dla czasu i `timedelta` dla różnic czasowych. Obiekt `datetime` można utworzyć za pomocą konstruktora `datetime(2024, 1, 15, 10, 30, 0)` lub pobrać bieżącą datę i czas za pomocą `datetime.now()`. Różnice między datami wyrażone są w obiektach `timedelta`, które umożliwiają dodawanie i odejmowanie interwałów czasowych.

Formatowanie dat do stringów i parsowanie stringów na daty realizują odpowiednio metody `strftime()` i `strptime()` z użyciem kodów formatujących, takich jak `%Y` dla roku, `%m` dla miesiąca czy `%d` dla dnia. Moduł `datetime` oferuje także strefy czasowe za pomocą klasy `timezone` oraz informacje o lokalnych ustawieniach regionalnych. W codziennej pracy administracyjnej moduł `datetime` jest używany do generowania znaczników czasowych w logach, obliczania czasu działania procesów, planowania zadań i formatowania dat w raportach. Znajomość tego modułu jest niezbędna przy każdym projekcie wymagającym operacji na datach i czasie.

36Formatowanie stringów - F-strings

Czym są F-strings?

F-string (formatted string) to nowoczesny sposób wstawiania zmiennych w tekst. Wprowadzony w Pythonie 3.6. Składnia:

f"Tekst {zmienna} tekst"

Zalety F-strings

  • Łatwiejsze i czytelniejsze niż konkatenacja
  • Można wstawiać wyrażenia
  • Łatwe formatowanie liczb

Formatowanie liczb

  • {x:.2f} - 2 miejsca po przecinku
  • {x:,} - separator tysięcy
  • {x:.1%} - procenty
# Podstawowe użycie
imie = "Anna"
wiek = 20
print(f"Nazywam się {imie} i mam {wiek} lat.")

# Wyrażenia w F-stringach
a = 10
b = 3
print(f"{a} + {b} = {a + b}")
print(f"{a} / {b} = {a / b:.2f}")

# Formatowanie liczb
cena = 1999.99
print(f"Cena: {cena:.2f} zł")
print(f"Cena: {cena:,.2f} zł")

# Procenty
procent = 0.256
print(f"Procent: {procent:.1%}")

# Wyrównywanie
imiona = ["Anna", "Bartek", "Celina"]
for i, imie in enumerate(imiona, 1):
    print(f"{i}. {imie:10} - OK")

# Data w F-stringu
import datetime
teraz = datetime.datetime.now()
print(f"Dzisiaj: {teraz:%Y-%m-%d}")

# Słowniki w F-stringach
student = {"imie": "Anna", "wiek": 20}
print(f"{student['imie']} ma {student['wiek']} lat")

F-stringi wprowadzone w Pythonie 3.6 stanowią najbardziej zaawansowany i czytelny sposób formatowania stringów, pozwalający na wstawianie wartości zmiennych bezpośrednio w tekście za pomocą składni `f"tekst {zmienna}"`. Wewnątrz nawiasów klamrowych w f-stringu można umieszczać nie tylko zmienne, ale także dowolne wyrażenia Pythona, na przykład `f"Wynik: {a + b}"` czy `f"{imie.upper()}"`. F-stringi są nie tylko czytelne, ale także szybsze od starszych metod formatowania, takich jak formatowanie procentowe `%s` czy metoda `format()`.

F-stringi oferują zaawansowane możliwości formatowania liczb i tekstów za pomocą specyfikatorów formatu, na przykład `f"{cena:.2f}"` zaokrągla liczbę do dwóch miejsc po przecinku, a `f"{tekst:>10}"` wyrównuje tekst do prawej na polu o szerokości 10 znaków. Możliwe jest także formatowanie dat za pomocą f-stringów przez wywołanie metody `strftime()` wewnątrz nawiasów klamrowych. Od Pythona 3.8 dostępne jest także formatowanie z debugowaniem `f"{zmienna=}"`, które wyświetla zarówno nazwę zmiennej, jak i jej wartość, co jest niezwykle przydatne przy debugowaniu. F-stringi są obecnie zalecanym sposobem formatowania tekstu w Pythonie.

37Moduł json - dane w formacie JSON

Co to jest JSON?

JSON (JavaScript Object Notation) to popularny format wymiany danych. Wygląda jak słownik Python z drobnymi różnicami.

Moduł json

Wbudowany moduł do konwersji między JSON a Pythonem:

  • json.dumps() - Python dict do string JSON
  • json.loads() - string JSON do Python dict
  • json.dump() - zapis do pliku
  • json.load() - odczyt z pliku

Praktyczne zastosowania

  • Konfiguracja aplikacji
  • API (komunikacja z serwerami)
  • Zapisywanie danych
import json

# Python dict -> JSON string
dane = {
    "imie": "Anna",
    "wiek": 20,
    "kierunek": "Informatyka",
    "oceny": [5, 4, 3, 5]
}

json_string = json.dumps(dane, ensure_ascii=False, indent=2)
print("JSON string:")
print(json_string)

# JSON string -> Python dict
json_text = '{"imie": "Bartek", "wiek": 22}'
slownik = json.loads(json_text)
print(f"\nSłownik: {slownik}")
print(f"Imię: {slownik['imie']}")

# Zapisywanie do pliku JSON
with open("dane.json", "w", encoding="utf-8") as plik:
    json.dump(dane, plik, ensure_ascii=False, indent=4)

print("\nZapisano do pliku dane.json")

# Odczyt z pliku JSON
with open("dane.json", "r", encoding="utf-8") as plik:
    wczytane = json.load(plik)

print(f"Wczytane dane: {wczytane}")

# Sprawdzanie typu
print(f"\nTyp przed: {type(dane)}")
print(f"Typ JSON: {type(json_string)}")

Moduł `json` w Pythonie umożliwia kodowanie i dekodowanie danych w formacie JSON, który jest standardowym formatem wymiany danych w aplikacjach internetowych i API REST. Funkcja `json.dumps()` konwertuje obiekt Pythona na string JSON, a `json.loads()` parsuje string JSON na obiekt Pythona. Podstawowe mapowanie typów między JSON a Pythonem obejmuje: obiekty JSON na słowniki, tablice na listy, stringi na stringi, liczby na int lub float, wartości true/false na True/False, a null na None.

Do odczytu i zapisu danych JSON bezpośrednio z plików służą funkcje `json.dump()` i `json.load()`, które przyjmują obiekt pliku jako argument. Parametr `indent` przy `dumps()` i `dump()` pozwala na sformatowanie JSON-a z wcięciami dla lepszej czytelności, co jest przydatne przy generowaniu plików konfiguracyjnych. Moduł `json` obsługuje także kodowanie niestandardowych obiektów przez podanie funkcji `default` do serializacji. JSON jest powszechnie używany w skryptach administracyjnych do przechowywania konfiguracji, komunikacji z API oraz wymiany danych między różnymi systemami.

38Moduł subprocess - uruchamianie poleceń systemowych

Co to jest subprocess?

subprocess pozwala uruchamiać polecenia systemowe z poziomu Pythona. Możesz wykonywać komendy systemowe i pobierać ich wyniki.

Podstawowe użycie

subprocess.run(["polecenie", "argument"])

Najważniejsze parametry

  • capture_output=True - przechwytuje wyjście
  • text=True - dekoduje bajty na tekst
  • check=True - rzuca błąd przy niepowodzeniu

WAŻNE!

Zawsze używaj listy argumentów, nie stringa! Zabezpiecza to przed atakami.

import subprocess
import sys

# Uruchomienie prostego polecenia
# W systemie Windows polecenie echo jest wbudowane w powłoce (cmd.exe),
# dlatego używamy shell=True - jest to wymagane dla wbudowanych poleceń Windows.
print("=== Pierwsze polecenie ===")
if sys.platform.startswith("win"):
    subprocess.run(["echo", "Witaj z Pythona!"], shell=True)
else:
    subprocess.run(["echo", "Witaj z Pythona!"])

# Pobranie wyniku polecenia
print("\n=== Z wynikiem ===")
if sys.platform.startswith("win"):
    wynik = subprocess.run(["echo", "Test Python"], capture_output=True, text=True, shell=True)
else:
    wynik = subprocess.run(["echo", "Test Python"], capture_output=True, text=True)
print(f"Wynik: {wynik.stdout}")

# Sprawdzenie kodu powrotu
print("\n=== Sprawdzenie kodu ===")
if sys.platform.startswith("win"):
    wynik = subprocess.run(["echo", "OK"], capture_output=True, text=True, shell=True)
else:
    wynik = subprocess.run(["echo", "OK"], capture_output=True, text=True)
print(f"Kod powrotu: {wynik.returncode}")  # 0 = sukces

# Obsługa błędów
print("\n=== Obsługa błędów ===")
try:
    if sys.platform.startswith("win"):
        subprocess.run(["nonexistent_command"], check=True, capture_output=True, text=True, shell=True)
    else:
        subprocess.run(["nonexistent_command"], check=True, capture_output=True, text=True)
except subprocess.CalledProcessError as e:
    print(f"Błąd polecenia: {e}")
except FileNotFoundError:
    print("Polecenie nie znalezione!")

# WAŻNE: Bezpieczne vs niebezpieczne
# Bezpiecznie (lista argumentów, shell=False):
# subprocess.run(["ping", "-n", "1", "8.8.8.8"])  # Windows
# subprocess.run(["ping", "-c", "1", "8.8.8.8"])  # Linux

# Niebezpiecznie (string ze shell=True - unikaj!):
# subprocess.run("ping -n 1 8.8.8.8", shell=True)

Moduł `subprocess` w Pythonie umożliwia uruchamianie zewnętrznych poleceń systemowych, przechwytywanie ich wyjścia i zarządzanie procesami potomnymi. Funkcja `subprocess.run()` jest najprostszym i najczęściej używanym sposobem wykonania polecenia, przyjmując listę argumentów, na przykład `subprocess.run(["ls", "-l"])`. Parametr `capture_output=True` pozwala na przechwycenie standardowego wyjścia i błędów, które są dostępne w obiekcie wyniku jako atrybuty `stdout` i `stderr`. Parametr `text=True` powoduje zwrócenie danych jako stringów zamiast bajtów.

Starsza funkcja `subprocess.call()` jest prostsza, ale nie oferuje przechwytywania wyjścia. W przypadku potrzeby bardziej zaawansowanego zarządzania procesami dostępne są klasy `Popen` i `PIPE`, które pozwalają na strumieniowanie danych, komunikację przez potoki i asynchroniczne wykonywanie poleceń. Przy uruchamianiu poleceń systemowych z poziomu Pythona należy zawsze zachować ostrożność i unikać przekazywania niesprawdzonych danych wejściowych użytkownika bezpośrednio do powłoki, aby zapobiec atakom typu command injection. Moduł `subprocess` jest niezbędnym narzędziem w skryptach automatyzujących zadania administracyjne.

39Moduł random - losowe liczby i wybór

Moduł random

random to wbudowany moduł do generowania liczb losowych i losowego wyboru elementów.

Przydatne funkcje

  • random.randint(a, b) - liczba całkowita od a do b
  • random.random() - liczba od 0 do 1
  • random.choice(lista) - losowy element listy
  • random.shuffle(lista) - losowe przetasowanie
  • random.sample(lista, n) - n losowych elementów

Zastosowania

  • Symulacje
  • Gry
  • Testowanie
  • Losowe dane do prezentacji
import random

# Losowa liczba całkowita
print("=== Losowe liczby ===")
for i in range(5):
    liczba = random.randint(1, 10)
    print(f"Losowa liczba 1-10: {liczba}")

# Liczba zmiennoprzecinkowa 0-1
print(f"\nFloat 0-1: {random.random():.4f}")

# Losowy wybór z listy
print("\n=== Losowy wybór ===")
owoce = ["jabłko", "banan", "wiśnia", "gruszka"]
print(f"Wylosowany owoc: {random.choice(owoce)}")

# Losowe hasło
znaki = "abcdefghijklmnopqrstuvwxyz0123456789"
haslo = "".join(random.choice(znaki) for _ in range(8))
print(f"Wygenerowane hasło: {haslo}")

# Przetasowanie listy
print("\n=== Przetasowanie ===")
karty = ["As", "Król", "Dama", "Walet", "10"]
print(f"Przed: {karty}")
random.shuffle(karty)
print(f"Po: {karty}")

# Losowa próbka (bez powtórzeń)
print("\n=== Losowa próbka ===")
liczby = list(range(1, 50))
wylosowane = random.sample(liczby, 6)
print(f"Wylosowane 6 z 49: {sorted(wylosowane)}")

# Symulacja rzutu kostką
print("\n=== Symulacja rzutu kostką ===")
for _ in range(5):
    wynik = random.randint(1, 6)
    print(f"Kostka: {wynik}")

Moduł `random` w Pythonie dostarcza funkcji do generowania liczb losowych i wykonywania losowych operacji, co znajduje zastosowanie w testowaniu, grach, symulacjach i kryptografii. Funkcja `random.random()` zwraca liczbę zmiennoprzecinkową z zakresu [0.0, 1.0), a `random.randint(a, b)` zwraca losową liczbę całkowitą z zakresu od a do b włącznie. Funkcja `random.choice(kolekcja)` losuje jeden element z podanej kolekcji, a `random.sample(kolekcja, k)` zwraca listę k unikalnych elementów bez powtórzeń.

Funkcja `random.shuffle(lista)` tasuje elementy listy w miejscu, zmieniając ich kolejność w sposób losowy, co jest przydatne przy tworzeniu losowych permutacji. Dla zastosowań kryptograficznych, gdzie wymagane jest prawdziwie losowe zachowanie, należy używać modułu `secrets` zamiast `random`, który opiera się na generatorze liczb pseudolosowych. Ustawienie ziarna generatora za pomocą `random.seed()` pozwala na uzyskanie powtarzalnych sekwencji losowych, co jest przydatne w testowaniu i debugowaniu. Moduł `random` jest prosty w użyciu, a jednocześnie oferuje wystarczającą funkcjonalność dla większości nietechnicznych zastosowań losowości.

40Moduł re - wyrażenia regularne (regex)

Co to są wyrażenia regularne?

Wyrażenia regularne (regex) to wzorce do wyszukiwania i dopasowywania tekstu. Pozwalają na zaawansowane wyszukiwanie w tekście.

Podstawowe wzorce

  • . - dowolny znak
  • \d - cyfra (0-9)
  • \w - znak słowny (a-z, A-Z, 0-9, _)
  • \s - biały znak
  • ^ - początek linii
  • $ - koniec linii
  • * - 0 lub więcej powtórzeń
  • + - 1 lub więcej powtórzeń
import re

# Wzorzec na numer telefonu (uproszczony)
wzor = r"\d{3}-\d{3}-\d{3}"
tekst = "Mój numer: 123-456-789"
dopasowanie = re.search(wzor, tekst)
if dopasowanie:
    print(f"Znaleziono: {dopasowanie.group()}")

# Wyszukiwanie adresu e-mail
wzor_email = r"\w+@\w+\.\w+"
emails = "Napisz: jan@test.com lub anna@domena.pl"
znalezione = re.findall(wzor_email, emails)
print(f"E-maile: {znalezione}")

# Walidacja numeru telefonu
def sprawdz_telefon(numer):
    wzor = r"^\d{3}-\d{3}-\d{3}$"
    if re.match(wzor, numer):
        return True
    return False

print(f"123-456-789: {sprawdz_telefon('123-456-789')}")
print(f"12-34: {sprawdz_telefon('12-34')}")

# Zamiana tekstu
tekst = "Kolor: czerwony, smak: kwaśny"
nowy = re.sub(r"\w+", "XXX", tekst)
print(f"Zamienione: {nowy}")

# Wyciąganie liczb z tekstu
tekst = "Mam 5 jabłek i 10 pomarańczy"
liczby = re.findall(r"\d+", tekst)
print(f"Liczby: {[int(x) for x in liczby]}")

Moduł `re` w Pythonie umożliwia pracę z wyrażeniami regularnymi, które są potężnym narzędziem do wyszukiwania wzorców w tekstach. Funkcja `re.search(wzor, tekst)` wyszukuje pierwsze wystąpienie wzorca w tekście i zwraca obiekt dopasowania lub None, jeśli wzorzec nie został znaleziony. Funkcja `re.findall(wzor, tekst)` zwraca listę wszystkich dopasowań, a `re.finditer(wzor, tekst)` zwraca iterator po obiektach dopasowania. Do dzielenia tekstu według wzorca służy `re.split()`, a do zamiany fragmentów `re.sub()`.

Wyrażenia regularne w Pythonie używają notacji `r"wzorzec"` z surowym stringiem, który zapobiega interpretacji sekwencji ucieczki przez Python. Podstawowe elementy wyrażeń regularnych to między innymi `\d` dla cyfr, `\w` dla znaków alfanumerycznych, `\s` dla białych znaków, `+` dla jednego lub więcej wystąpień, `*` dla zera lub więcej, `?` dla opcjonalnego wystąpienia oraz nawiasy do grupowania. Wyrażenia regularne są szeroko stosowane w walidacji danych wejściowych, parsowaniu logów, ekstrakcji informacji z plików i wszędzie tam, gdzie potrzebne jest zaawansowane wyszukiwanie wzorców w tekście.

41Zmienne środowiskowe - os.environ

Co to są zmienne środowiskowe?

Zmienne środowiskowe to ustawienia systemu operacyjnego dostępne dla wszystkich programów. Python ma do nich dostęp przez os.environ.

Przykłady zmiennych

  • PATH - ścieżki do programów
  • USERNAME / USER - nazwa użytkownika
  • HOME / USERPROFILE - katalog domowy
  • TEMP - katalog tymczasowy

Zastosowania

  • Konfiguracja (nie w kodzie!)
  • Przechowywanie haseł/API keys
  • Wykrywanie systemu operacyjnego
import os

# Wszystkie zmienne środowiskowe (słownik)
print("=== Zmienne środowiskowe ===")
for klucz, wartosc in list(os.environ.items())[:5]:
    print(f"{klucz}: {wartosc}")

# Pobranie konkretnej zmiennej
print("\n=== Pobieranie zmiennych ===")
uzytkownik = os.environ.get("USERNAME") or os.environ.get("USER")
print(f"Nazwa użytkownika: {uzytkownik}")

katalog_domowy = os.environ.get("HOME") or os.environ.get("USERPROFILE")
print(f"Katalog domowy: {katalog_domowy}")

# System operacyjny
system = os.environ.get("OS") or os.name
print(f"System: {system}")

# Własna zmienna środowiskowa
# W terminalu: export MOJA_ZMIENNA=wartość
moja_zmienna = os.environ.get("MOJA_ZMIENNA", "domyślna")
print(f"Moja zmienna: {moja_zmienna}")

# Ustawianie zmiennej (tylko dla tego procesu)
os.environ["TEST"] = "wartość_testowa"
print(f"TEST: {os.environ.get('TEST')}")

# Ścieżka do pliku tymczasowego
temp_dir = os.environ.get("TEMP") or os.environ.get("TMP") or "/tmp"
print(f"Katalog tymczasowy: {temp_dir}")

Zmienne środowiskowe w Pythonie są dostępne za pomocą słownikopodobnego obiektu `os.environ`, który zawiera wszystkie zmienne środowiskowe bieżącego procesu. Odczyt zmiennej środowiskowej wykonuje się przez `os.environ.get("NAZWA")` lub `os.environ["NAZWA"]`, przy czym pierwsza metoda zwraca None dla nieistniejącej zmiennej, a druga zgłasza wyjątek `KeyError`. Przypisanie wartości do zmiennej środowiskowej w bieżącym procesie jest możliwe przez `os.environ["NAZWA"] = "wartosc"`, ale zmiana ta wpływa tylko na bieżący proces i jego procesy potomne, nie modyfikując systemowych zmiennych.

W praktyce zmienne środowiskowe są często używane do przechowywania danych konfiguracyjnych, takich jak hasła, klucze API czy ścieżki dostępu, które nie powinny być umieszczane bezpośrednio w kodzie źródłowym. Pozwala to na oddzielenie konfiguracji od kodu i dostosowanie działania programu do środowiska bez modyfikacji plików źródłowych. Biblioteka `python-dotenv` umożliwia łatwe ładowanie zmiennych środowiskowych z pliku `.env`, co jest standardem w nowoczesnych projektach Pythonowych. Moduł `os` oferuje także funkcje do zarządzania ścieżkami systemowymi i wykonywania poleceń powłoki z uwzględnieniem zmiennych środowiskowych.

42Wykrywanie systemu operacyjnego

Moduł sys

sys.platform zwraca nazwę systemu operacyjnego:

  • win32 - Windows
  • linux - Linux
  • darwin - macOS

Wykrywanie z os.name

os.name zwraca:

  • nt - Windows NT (Windows)
  • posix - Unix/Linux/macOS

Praktyczne zastosowania

Pozwala pisać kod działający na różnych systemach, dostosowując polecenia i ścieżki.

import sys
import os

print("=== Informacje o systemie ===")
print(f"sys.platform: {sys.platform}")
print(f"os.name: {os.name}")

# Wykrywanie Windows
if sys.platform.startswith("win"):
    print("\nWykryto: Windows")
    print("Używam poleceń Windows: dir, type, copy")
    
# Wykrywanie Linux
elif sys.platform.startswith("linux"):
    print("\nWykryto: Linux")
    print("Używam poleceń Linux: ls, cat, cp")

# Wykrywanie macOS
elif sys.platform == "darwin":
    print("\nWykryto: macOS")
    print("Używam poleceń macOS")

# Ścieżki zależne od systemu
print("\n=== Ścieżki systemowe ===")
if sys.platform.startswith("win"):
    sciezka_konfig = os.path.join(os.environ["APPDATA"], "moja_app")
else:
    sciezka_konfig = os.path.join(os.environ["HOME"], ".moja_app")
    
print(f"Ścieżka konfiguracji: {sciezka_konfig}")

# Komendy zależne od systemu
def wyczysc_ekran():
    if sys.platform.startswith("win"):
        os.system("cls")
    else:
        os.system("clear")

print("\nGotowe do wyczyszczenia ekranu.")

Wykrywanie systemu operacyjnego w Pythonie jest realizowane za pomocą modułu `platform` lub atrybutu `sys.platform`, które dostarczają informacji o bieżącym systemie. Moduł `platform` oferuje funkcje takie jak `platform.system()` zwracająca nazwę systemu, `platform.release()` dla numeru wersji, `platform.processor()` dla typu procesora i `platform.machine()` dla architektury. Atrybut `sys.platform` zwraca krótki identyfikator, taki jak `"win32"` dla Windows, `"linux"` dla Linux czy `"darwin"` dla macOS.

Znajomość systemu operacyjnego jest kluczowa przy pisaniu skryptów przenośnych, które mają działać na różnych platformach bez modyfikacji. Różne systemy operacyjne używają różnych separatorów ścieżek, poleceń systemowych i konwencji dotyczących plików, dlatego kod powinien dostosowywać swoje zachowanie do wykrytego systemu. Moduł `os` oferuje funkcje takie jak `os.name` do podstawowej identyfikacji rodziny systemów oraz `os.sep` do pobrania separatora ścieżek. Przy pisaniu skryptów wieloplatformowych warto korzystać z modułów takich jak `pathlib` i `shutil`, które abstrahują od różnic systemowych i zapewniają spójne API.

43Moduł shutil - zaawansowane operacje na plikach

Moduł shutil

shutil (shell utilities) oferuje funkcje wyższego poziomu do operacji na plikach i katalogach.

Przydatne funkcje

  • shutil.copy(src, dst) - kopiowanie pliku
  • shutil.copytree(src, dst) - kopiowanie całego katalogu
  • shutil.move(src, dst) - przenoszenie/zmiana nazwy
  • shutil.rmtree(path) - usuwanie katalogu z zawartością
  • shutil.make_archive() - tworzenie archiwum
  • shutil.disk_usage(path) - informacje o dysku
import shutil
import os
from pathlib import Path

# Tworzenie struktury katalogów do testów
test_dir = Path("test_shutil")
test_dir.mkdir(exist_ok=True)
(test_dir / "plik.txt").write_text("Testowa zawartość")

# Kopiowanie pliku
print("=== Kopiowanie ===")
shutil.copy(test_dir / "plik.txt", test_dir / "kopia.txt")
print("Skopiowano plik.txt -> kopia.txt")

# Kopiowanie katalogu
print("\n=== Kopiowanie katalogu ===")
os.makedirs(test_dir / "folder", exist_ok=True)
shutil.copytree(test_dir, test_dir.parent / "kopia_folderu")
print(f"Skopiowano {test_dir} -> kopia_folderu")

# Przenoszenie/zmiana nazwy
print("\n=== Przenoszenie ===")
shutil.move(test_dir / "kopia.txt", test_dir / "przeniesiony.txt")
print("Przeniesiono kopia.txt -> przeniesiony.txt")

# Informacje o dysku
print("\n=== Informacje o dysku ===")
usage = shutil.disk_usage("/")
print(f"Całkowite: {usage.total / (1024**3):.2f} GB")
print(f"Używane: {usage.used / (1024**3):.2f} GB")
print(f"Wolne: {usage.free / (1024**3):.2f} GB")

# Sprzątanie
print("\n=== Sprzątanie ===")
shutil.rmtree(test_dir)
print("Usunięto test_shutil")

Moduł `shutil` w Pythonie oferuje zaawansowane operacje na plikach i katalogach, wykraczające poza podstawowe możliwości modułu `os`. Funkcja `shutil.copy()` kopiuje plik do nowej lokalizacji, zachowując podstawowe atrybuty, a `shutil.copy2()` dodatkowo zachowuje metadane, takie jak czas modyfikacji. Funkcja `shutil.copytree()` rekurencyjnie kopiuje całe drzewo katalogów, co jest przydatne przy tworzeniu kopii zapasowych. Do przenoszenia plików i katalogów służy `shutil.move()`, która działa zarówno w obrębie tego samego systemu plików, jak i między różnymi dyskami.

Usuwanie całych drzew katalogów realizuje `shutil.rmtree()`, które działa podobnie do `rm -rf` w systemach Unix. Funkcja `shutil.disk_usage()` zwraca informacje o pojemności i wolnym miejscu na dysku, co jest przydatne przy monitorowaniu przestrzeni dyskowej. `shutil.make_archive()` umożliwia tworzenie archiwów ZIP, TAR i innych formatów, a `shutil.which()` wyszukuje plik wykonywalny w zmiennej PATH, podobnie do polecenia `which` w systemach Unix. Moduł `shutil` jest niezbędnym narzędziem w skryptach administracyjnych zajmujących się zarządzaniem plikami na poziomie systemowym.

44Moduł argparse - profesjonalne przetwarzanie argumentów

Co to jest argparse?

argparse to standardowy moduł do przetwarzania argumentów wiersza poleceń. Automatycznie tworzy pomoc i waliduje argumenty.

Zalety argparse

  • Automatyczna pomoc (-h, --help)
  • Walidacja typów
  • Domyślne wartości
  • Opisowe komunikaty błędów

Składnia

parser.add_argument("--nazwa", "-n", type=int, default=0)
import argparse

# Tworzenie parsera
parser = argparse.ArgumentParser(
    description="Program do kalkulacji podatku VAT",
    epilog="Przykład: python vat.py --kwota 100 --stawka 23"
)

# Dodawanie argumentów
parser.add_argument("kwota", type=float, help="Kwota netto")
parser.add_argument("-s", "--stawka", type=float, default=23, 
                    help="Stawka VAT (domyślnie 23)")
parser.add_argument("-v", "--verbose", action="store_true",
                    help="Tryb szczegółowy")

# Parsowanie argumentów
args = parser.parse_args()

# Użycie argumentów
print(f"Kwota netto: {args.kwota}")
print(f"Stawka VAT: {args.stawka}%")

if args.verbose:
    print(f"Kwota brutto: {args.kwota * (1 + args.stawka/100):.2f}")
else:
    print(f"Wynik: {args.kwota * (1 + args.stawka/100):.2f}")

# === PRZYKŁADOWE URUCHOMIENIE ===

Moduł `argparse` w Pythonie jest profesjonalnym narzędziem do przetwarzania argumentów wiersza poleceń, oferującym znacznie więcej możliwości niż prosty `sys.argv`. Podstawowym elementem `argparse` jest obiekt `ArgumentParser`, który definiuje oczekiwane argumenty i automatycznie generuje komunikaty pomocy. Dodawanie argumentów odbywa się za pomocą metody `add_argument()`, która pozwala na określenie typu, wartości domyślnej, komunikatu pomocy i wielu innych właściwości. Po skonfigurowaniu parsera wywołuje się metodę `parse_args()`, która zwraca obiekt z atrybutami odpowiadającymi nazwom argumentów.

Argparse obsługuje argumenty pozycyjne, opcjonalne, flagi logiczne, wartości domyślne, ograniczenia zakresu i wiele innych zaawansowanych funkcji. Automatycznie generuje komunikaty `-h` i `--help` z czytelnym opisem wszystkich argumentów, co znacząco podnosi użyteczność skryptów. Parametr `type` pozwala na automatyczną konwersję argumentów na odpowiedni typ, a `choices` ogranicza możliwe wartości do podanej listy. Argparse jest standardowym narzędziem do budowania interfejsów wiersza poleceń w Pythonie i powinien być używany w każdym skrypcie, który przyjmuje argumenty od użytkownika.

45Moduł logging - profesjonalne logowanie

Dlaczego logging zamiast print?

logging pozwala na kontrolowane wyświetlanie informacji o działaniu programu. Jest bardziej profesjonalne niż print().

Poziomy logowania

  • DEBUG - szczegółowe info (najniższy)
  • INFO - potwierdzenia
  • WARNING - ostrzeżenia
  • ERROR - błędy
  • CRITICAL - krytyczne błędy (najwyższy)

Zalety

  • Można zapisywać do pliku
  • Można filtrować poziomy
  • Znacznik czasu
import logging

# Podstawowa konfiguracja
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s [%(levelname)s] %(message)s',
    datefmt='%H:%M:%S'
)

logger = logging.getLogger(__name__)

# Różne poziomy logowania
logger.debug("Informacja debug - szczegóły")
logger.info("Informacja ogólna - np. start programu")
logger.warning("Ostrzeżenie - coś może być nie tak")
logger.error("Błąd - coś poszło nie tak")
logger.critical("Błąd krytyczny - program nie może kontynuować")

# Przykład praktyczny
def funkcja_pobierajaca_dane(id):
    logger.info(f"Rozpoczynam pobieranie danych dla ID: {id}")
    
    try:
        logger.debug(f"Łączenie z bazą danych...")
        logger.debug(f"Zapytanie wykonane")
        logger.info(f"Pobieranie zakończone sukcesem")
        return {"id": id, "dane": "przykładowe"}
        
    except Exception as e:
        logger.error(f"Błąd pobierania danych: {e}")
        return None

Moduł `logging` w Pythonie jest zaawansowanym systemem do rejestrowania zdarzeń i komunikatów z programu, oferującym znacznie więcej możliwości niż proste `print()`. Podstawowym elementem jest obiekt `Logger`, który tworzy się za pomocą `logging.getLogger("nazwa")` i który dostarcza metod takich jak `debug()`, `info()`, `warning()`, `error()` i `critical()` dla różnych poziomów ważności. Konfiguracja logowania odbywa się za pomocą `basicConfig()`, która pozwala na ustawienie poziomu logowania, formatu komunikatów i miejsca docelowego, na przykład pliku lub konsoli.

Logowanie na odpowiednich poziomach pozwala na filtrowanie komunikatów według ich ważności, co jest szczególnie przydatne w środowisku produkcyjnym, gdzie interesują nas tylko błędy i ostrzeżenia. Domyślnie logowane są tylko komunikaty od poziomu WARNING wzwyż, ale można to zmienić przez ustawienie poziomu na loggerze. Format komunikatu logu może zawierać znacznik czasu, nazwę loggera, poziom ważności i treść wiadomości, co ułatwia analizę logów. Moduł `logging` obsługuje także rotację plików logów, wysyłanie logów przez sieć i integrację z zewnętrznymi systemami monitorującymi.

46Moduł urllib - komunikacja sieciowa

Moduł urllib

urllib to wbudowany moduł do pobierania danych z internetu (protokoły HTTP, HTTPS). Nie wymaga dodatkowych bibliotek.

Przydatne funkcje

  • urllib.request.urlopen(url) - otwiera URL
  • response.read() - czyta odpowiedź
  • urllib.parse.urlencode() - koduje parametry

Uwaga

Dla bardziej zaawansowanych operacji HTTP zalecana jest biblioteka requests (trzeba zainstalować: pip install requests).

import urllib.request
import json

# Pobieranie prostej strony
print("=== Pobieranie strony ===")
url = "https://jsonplaceholder.typicode.com/todos/1"

try:
    with urllib.request.urlopen(url, timeout=10) as response:
        dane = response.read().decode('utf-8')
        print(f"Odpowiedź: {dane}")
        
        # Parsowanie JSON
        dane_json = json.loads(dane)
        print(f"\nTitle: {dane_json['title']}")
        print(f"Completed: {dane_json['completed']}")
        
except Exception as e:
    print(f"Błąd: {e}")

# Sprawdzanie czy strona istnieje
print("\n=== Sprawdzanie URL ===")
def sprawdz_url(adres_url):
    try:
        with urllib.request.urlopen(adres_url, timeout=5) as response:
            return f"OK - kod {response.status}"
    except urllib.error.HTTPError as e:
        return f"HTTP Error: {e.code}"
    except urllib.error.URLError as e:
        return f"URL Error: {e.reason}"
    except Exception as e:
        return f"Błąd: {e}"

print(sprawdz_url("https://google.com"))
print(sprawdz_url("https://httpbin.org/status/404"))

Moduł `urllib` w Pythonie umożliwia wykonywanie zapytań HTTP i komunikację z serwerami sieciowymi, co jest niezbędne w skryptach integrujących się z API REST lub pobierających dane z internetu. Podstawowa funkcja `urllib.request.urlopen()` otwiera połączenie z podanym URL i zwraca obiekt odpowiedzi, z którego można odczytać dane. Bardziej zaawansowane operacje, takie jak wysyłanie danych POST, obsługa nagłówków HTTP i zarządzanie ciasteczkami, wymagają użycia klasy `Request`. Do parsowania URLi służy moduł `urllib.parse` z funkcjami `urlparse()`, `urlencode()` i `quote()`.

Alternatywą dla `urllib` jest popularna biblioteka zewnętrzna `requests`, która oferuje znacznie bardziej przyjazne API i jest powszechnie używana w nowoczesnych projektach Pythonowych. W codziennej administracji moduły sieciowe są używane do wysyłania powiadomień, pobierania danych z API usług chmurowych, sprawdzania dostępności stron internetowych i integracji różnych systemów. Przy pracy z żądaniami HTTP należy zawsze obsługiwać potencjalne błędy sieciowe, takie jak timeout, brak połączenia czy nieprawidłowe kody odpowiedzi HTTP, używając bloków try-except.

47Tworzenie własnych modułów

Co to jest moduł?

Moduł to plik Python (.py) zawierający funkcje, zmienne i klasy, które można importować w innych plikach.

Tworzenie modułu

  1. Stwórz plik moj_modul.py
  2. Dodaj funkcje i zmienne
  3. Zaimportuj w innym pliku

Importowanie

import moj_modul
moj_modul.funkcja()
# === PLIK: moje_narzedzia.py ===

STALA_PI = 3.14159
WERSJA = "1.0"

def powitaj(imie):
    """Funkcja wita użytkownika."""
    return f"Cześć, {imie}!"

def oblicz_pole_kola(promien):
    """Oblicza pole koła."""
    return STALA_PI * promien ** 2

# === PLIK: main.py ===

import moje_narzedzia

# Użycie funkcji z modułu
print(moje_narzedzia.powitaj("Anna"))
print(f"Wersja: {moje_narzedzia.WERSJA}")

# Import z aliasem
import moje_narzedzia as mn
print(mn.oblicz_pole_kola(5))

# Import konkretnych elementów
from moje_narzedzia import powitaj, oblicz_pole_kola
print(powitaj("Bartek"))
print(oblicz_pole_kola(10))

Tworzenie własnych modułów w Pythonie jest podstawowym mechanizmem organizacji kodu w większych projektach, pozwalającym na grupowanie powiązanych funkcji, klas i zmiennych w osobnych plikach. Każdy plik z rozszerzeniem `.py` może być modułem, a funkcje i klasy zdefiniowane w tym pliku mogą być importowane do innych skryptów za pomocą instrukcji `import nazwa_modulu`. Python przeszukuje ścieżki zdefiniowane w `sys.path` w poszukiwaniu modułów, co obejmuje bieżący katalog, standardowe biblioteki i dodatkowe ścieżki skonfigurowane przez użytkownika.

Moduły mogą być organizowane w pakiety, czyli katalogi zawierające plik `__init__.py`, który może zawierać kod inicjalizacyjny lub definiować, co jest eksportowane z pakietu. Importowanie z pakietów odbywa się za pomocą notacji kropkowej, na przykład `import pakiet.modul`. Python oferuje kilka form instrukcji `import`, w tym `import modul`, `from modul import funkcja`, `from modul import *` oraz `import modul as alias`. Dzielenie kodu na moduły znacząco poprawia czytelność, ułatwia testowanie i umożliwia wielokrotne użycie kodu w różnych projektach.

48Moduł sys - informacje o Pythonie

Moduł sys

sys dostarcza informacji o samym Pythonie i jego środowisku.

Przydatne atrybuty

  • sys.version - wersja Pythona
  • sys.executable - ścieżka do Pythona
  • sys.platform - platforma systemowa
  • sys.path - lista ścieżek wyszukiwania modułów
  • sys.argv - argumenty wiersza poleceń

Przydatne funkcje

  • sys.exit() - zakończenie programu
  • sys.stdout - standardowe wyjście
  • sys.stdin - standardowe wejście
import sys

print("=== Informacje o Pythonie ===")
print(f"Wersja Pythona: {sys.version}")
print(f"Wersja (krótka): {sys.version_info}")
print(f"Wersja główna: {sys.version_info.major}.{sys.version_info.minor}")

print(f"\nŚcieżka do Pythona: {sys.executable}")

print(f"\nPlatforma: {sys.platform}")

print("\n=== Ścieżki wyszukiwania modułów ===")
for i, sciezka in enumerate(sys.path[:5]):
    print(f"{i+1}. {sciezka}")

# Zakończenie programu
# sys.exit(0)  # 0 = normalne zakończenie
# sys.exit(1)  # 1 = błąd

# Przekierowanie wyjścia
print("\n=== Standardowe wyjście ===")
print(f"Obiekt stdout: {sys.stdout}")

# Sprawdzanie wersji
if sys.version_info.major >= 3:
    print("\nUżywasz Python 3!")
    if sys.version_info.minor >= 8:
        print("Masz przynajmniej Python 3.8")

Moduł `sys` w Pythonie dostarcza dostępu do zmiennych i funkcji interpreterów, które są silnie związane z systemem operacyjnym i środowiskiem wykonawczym. Atrybut `sys.version` zwraca pełny numer wersji Pythona, `sys.platform` identyfikuje system operacyjny, a `sys.executable` wskazuje ścieżkę do interpretera. Lista `sys.path` zawiera ścieżki wyszukiwania modułów, a jej modyfikacja pozwala na dodawanie własnych lokalizacji do importowania. Lista `sys.argv` przechowuje argumenty wiersza poleceń, co jest podstawą tworzenia skryptów akceptujących parametry.

Funkcja `sys.exit()` pozwala na zakończenie programu z określonym kodem wyjścia, gdzie 0 oznacza sukces, a wartości niezerowe oznaczają błędy. `sys.stdin`, `sys.stdout` i `sys.stderr` to obiekty strumieni wejścia/wyjścia, które można przekierować do plików lub innych strumieni. Funkcja `sys.getsizeof()` zwraca rozmiar obiektu w bajtach, co jest przydatne przy optymalizacji pamięci. Moduł `sys` zawiera także zaawansowane narzędzia do debugowania, takie jak `sys.settrace()` i `sys.setprofile()`, oraz funkcje do zarządzania pamięcią i garbage collection.

49Moduł collections - zaawansowane struktury danych

Moduł collections

collections dostarcza specjalne typy danych, które rozszerzają możliwości standardowych.

Najważniejsze klasy

  • Counter - licznik elementów
  • defaultdict - słownik z domyślną wartością
  • namedtuple - krotka z nazwami pól
  • deque - kolejka dwukierunkowa

Counter - zliczanie elementów

Przydatny do analizy tekstów, logów, danych.

from collections import Counter, defaultdict, namedtuple, deque

# === Counter - zliczanie elementów ===
print("=== Counter ===")
owoce = ["jabłko", "banan", "jabłko", "wiśnia", "banan", "jabłko"]
zlicz = Counter(owoce)
print(f"Zliczone: {zlicz}")
print(f"Najczęstsze: {zlicz.most_common(2)}")

# Zliczanie słów w tekście
tekst = "to jest tekst z kilkoma słowami i tekst i jeszcze więcej słów"
slowa = tekst.split()
zlicz_slow = Counter(slowa)
print(f"Słowa: {zlicz_slow.most_common(3)}")

# === defaultdict - słownik z domyślną wartością ===
print("\n=== defaultdict ===")
dd = defaultdict(int)  # domyślna wartość to 0
dd["jabłka"] += 1
dd["banany"] += 5
print(f"dd: {dict(dd)}")

# === namedtuple - krotka z nazwami pól ===
print("\n=== namedtuple ===")
Osoba = namedtuple("Osoba", ["imie", "wiek", "miasto"])
anna = Osoba("Anna", 20, "Warszawa")
print(f"Osoba: {anna}")
print(f"Imię: {anna.imie}, Wiek: {anna.wiek}")

# === deque - kolejka dwukierunkowa ===
print("\n=== deque ===")
kolejka = deque()
kolejka.append("pierwszy")
kolejka.append("drugi")
kolejka.appendleft("przed pierwszym")
print(f"Kolejka: {list(kolejka)}")
print(f"Pierwszy: {kolejka[0]}")
print(f"Ostatni: {kolejka[-1]}")

Moduł `collections` w Pythonie dostarcza zaawansowanych typów danych rozszerzających możliwości standardowych kolekcji. `namedtuple()` tworzy klasę krotki z nazwanymi polami, która łączy prostotę krotki z czytelnością dostępu przez atrybuty. `defaultdict` to słownik, który automatycznie tworzy domyślne wartości dla nieistniejących kluczy, co eliminuje konieczność ręcznego sprawdzania i inicjalizacji. `Counter` to słownik do zliczania wystąpień elementów w kolekcji, oferujący metody takie jak `most_common()` do znajdowania najczęściej występujących elementów.

`OrderedDict` to słownik zapamiętujący kolejność wstawiania kluczy, co jest przydatne w sytuacjach, gdy kolejność elementów ma znaczenie. `deque` to dwustronna kolejka zapewniająca szybkie dodawanie i usuwanie elementów z obu końców, idealna do implementacji kolejek FIFO i stosów LIFO. `ChainMap` łączy wiele słowników w jeden widok, umożliwiając przeszukiwanie ich w określonej kolejności. Moduł `collections` jest szeroko stosowany w zaawansowanych projektach Pythonowych, gdzie standardowe typy kolekcji okazują się niewystarczające.

50Moduł string - operacje na tekstach

Moduł string

string dostarcza użyteczne stałe i funkcje związane z tekstem.

Przydatne stałe

  • string.ascii_letters - wszystkie litery
  • string.ascii_lowercase - małe litery a-z
  • string.ascii_uppercase - wielkie litery A-Z
  • string.digits - cyfry 0-9
  • string.punctuation - znaki interpunkcyjne
  • string.whitespace - białe znaki

Template - prostsze formatowanie

Alternatywa dla F-strings przy prostym formatowaniu.

import string

print("=== Stałe string ===")
print(f"Litery: {string.ascii_letters}")
print(f"Małe: {string.ascii_lowercase}")
print(f"Wielkie: {string.ascii_uppercase}")
print(f"Cyfry: {string.digits}")
print(f"Interpunkcja: {string.punctuation}")

# Generowanie losowego hasła
import random

def generuj_haslo(dlugosc=12):
    """Generuje losowe hasło."""
    znaki = string.ascii_letters + string.digits + string.punctuation
    return "".join(random.choice(znaki) for _ in range(dlugosc))

print("\n=== Generowanie haseł ===")
for i in [8, 12, 16]:
    print(f"Hasło {i} znaków: {generuj_haslo(i)}")

# Template - prostsze formatowanie
print("\n=== Template ===")
szablon = string.Template("Witaj, $imie! Masz $liczba wiadomości.")
wiadomosc = szablon.substitute(imie="Anna", liczba=5)
print(wiadomosc)

# Walidacja tekstu
print("\n=== Walidacja tekstu ===")
tekst = "Test123"
print(f"Same litery? {tekst.isalpha()}")
print(f"Same cyfry? {tekst.isdigit()}")
print(f"Alfanumeryczne? {tekst.isalnum()}")

Moduł `string` w Pythonie dostarcza stałych i funkcji pomocniczych do pracy z tekstami, które uzupełniają wbudowane metody stringów. Stała `string.ascii_letters` zawiera wszystkie litery alfabetu angielskiego, małe i wielkie, `string.ascii_lowercase` tylko małe, a `string.ascii_uppercase` tylko wielkie. `string.digits` zawiera cyfry od 0 do 9, `string.punctuation` wszystkie znaki interpunkcyjne, a `string.hexdigits` cyfry szesnastkowe. Stała `string.whitespace` zawiera wszystkie białe znaki, w tym spację, tabulator i znaki nowej linii.

Klasa `string.Formatter` w module `string` stanowi podstawę dla zaawansowanego formatowania stringów i może być rozszerzana do tworzenia własnych języków formatowania. Moduł `string` zawiera także szablony `Template`, które oferują prostszą alternatywę dla formatowania za pomocą składni `$zmienna`. Szablony są szczególnie przydatne w sytuacjach, gdzie formatowany string pochodzi z zewnętrznego źródła i nie chcemy ryzykować błędów składniowych f-stringów. Mimo że większość codziennych operacji tekstowych można wykonać za pomocą wbudowanych metod stringów i f-stringów, moduł `string` dostarcza przydatnych uzupełnień dla bardziej wyspecjalizowanych zastosowań.

51Walidacja danych wejściowych

Co to jest walidacja?

Walidacja to sprawdzanie, czy dane wejściowe są poprawne przed ich użyciem. Zapobiega błędom i atakom.

Przykłady walidacji

  • Czy e-mail ma poprawny format?
  • Czy numer telefonu ma odpowiednią długość?
  • Czy wartość jest w określonym zakresie?
  • Czy pole nie jest puste?

Zasady dobrej walidacji

  • Sprawdzaj jak najwcześniej
  • Podawaj jasne komunikaty błędów
  • Nie ujawniaj zbyt wielu informacji (bezpieczeństwo)
import re

def waliduj_email(email):
    """Sprawdza czy email jest poprawny."""
    wzor = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
    if re.match(wzor, email):
        return True, "Poprawny email"
    return False, "Niepoprawny email"

def waliduj_telefon(telefon):
    """Sprawdza czy numer telefonu jest poprawny."""
    # Usuń wszystkie nie-cyfry
    cyfry = re.sub(r"\D", "", telefon)
    if len(cyfry) == 9:
        return True, f"Poprawny: {cyfry}"
    return False, "Numer powinien mieć 9 cyfr"

def waliduj_wiek(wiek):
    """Sprawdza czy wiek jest realistyczny."""
    try:
        wiek_int = int(wiek)
        if wiek_int < 0:
            return False, "Wiek nie może być ujemny"
        if wiek_int > 150:
            return False, "Wiek jest niemożliwie duży"
        return True, f"Poprawny wiek: {wiek_int}"
    except ValueError:
        return False, "Wiek musi być liczbą"

def waliduj_haslo(haslo):
    """Sprawdza siłę hasła."""
    if len(haslo) < 8:
        return False, "Hasło musi mieć min. 8 znaków"
    if not re.search(r"[A-Z]", haslo):
        return False, "Hasło musi zawierać wielką literę"
    if not re.search(r"[a-z]", haslo):
        return False, "Hasło musi zawierać małą literę"
    if not re.search(r"\d", haslo):
        return False, "Hasło musi zawierać cyfrę"
    return True, "Silne hasło"

# Testy
print("=== Walidacja email ===")
for email in ["test@wp.pl", "niepoprawny", "a@b.c"]:
    poprawne, msg = waliduj_email(email)
    print(f"{email}: {msg}")

print("\n=== Walidacja hasła ===")
for haslo in ["haslo", "Haslo123", "SilneHaslo123!"]:
    poprawne, msg = waliduj_haslo(haslo)
    print(f"{haslo}: {msg}")

Walidacja danych wejściowych w Pythonie jest kluczowym elementem bezpiecznego i niezawodnego programowania, który zapobiega awariom spowodowanym nieprawidłowymi danymi od użytkownika. Podstawowa walidacja polega na sprawdzeniu typu danych za pomocą `isinstance()` oraz zakresu wartości za pomocą operatorów porównania. W przypadku danych liczbowych warto sprawdzić, czy mieszczą się w oczekiwanym przedziale, a w przypadku tekstowych, czy nie są puste i czy nie zawierają niedozwolonych znaków. Funkcje walidujące powinny zwracać jasne komunikaty błędów, które informują użytkownika, co dokładnie jest nieprawidłowe.

Dla bardziej zaawansowanej walidacji można korzystać z wyrażeń regularnych modułu `re` do sprawdzania formatu adresów email, numerów telefonów czy kodów pocztowych. Biblioteka `pydantic` stanowi profesjonalne narzędzie do walidacji danych w większych projektach, oferując deklaratywne definiowanie modeli danych z automatyczną walidacją typów i wartości. Dobrą praktyką jest walidacja danych na granicy systemu, czyli zaraz po ich wprowadzeniu, zanim trafią do dalszego przetwarzania. Systematyczna walidacja danych wejściowych znacząco podnosi jakość i niezawodność oprogramowania.

52Porównanie: sys.argv vs argparse

Dwie metody obsługi argumentów

Python oferuje dwie główne metody przetwarzania argumentów wiersza poleceń.

sys.argv - prosta metoda

  • Wbudowany, nie wymaga importu
  • Prosty w użyciu
  • Brak automatycznej pomocy
  • Brak walidacji typów

argparse - zaawansowana metoda

  • Wymaga importu
  • Automatyczna pomoc (-h, --help)
  • Walidacja typów
  • Domyślne wartości
  • Grupy argumentów, subparsers

Kiedy co używać?

sys.argv - proste skrypty
argparse - profesjonalne narzędzia

# === PROSTA METODA: sys.argv ===
print("=== sys.argv ===")
import sys
print(f"Argumenty: {sys.argv}")

# Przykład prostego użycia
if len(sys.argv) > 1:
    print(f"Plik do przetworzenia: {sys.argv[1]}")

# === PROFESJONALNA METODA: argparse ===
print("\n=== argparse ===")
import argparse

parser = argparse.ArgumentParser(description="Kopiowanie plików")
parser.add_argument("zrodlo", help="Plik źródłowy")
parser.add_argument("cel", help="Plik docelowy")
parser.add_argument("-v", "--verbose", action="store_true", help="Tryb verbose")
parser.add_argument("-c", "--copy", type=int, default=1, help="Liczba kopii")

args = parser.parse_args()

print(f"Źródło: {args.zrodlo}")
print(f"Cel: {args.cel}")
print(f"Verbose: {args.verbose}")
print(f"Liczba kopii: {args.copy}")

# Uruchom:
# python skrypt.py plik.txt kopia.txt -v -c 3

Porównanie `sys.argv` i `argparse` w Pythonie uwidacznia zalety korzystania z zaawansowanych narzędzi do przetwarzania argumentów wiersza poleceń w profesjonalnych skryptach. `sys.argv` jest prostą listą stringów dostępną bez żadnych dodatkowych importów poza modułem `sys`, co czyni ją idealną do bardzo prostych skryptów, gdzie obsługujemy jeden lub dwa argumenty. `argparse` wymaga więcej kodu na początku, ale w zamian oferuje automatyczne generowanie komunikatów pomocy, walidację typów, wartości domyślne i obsługę argumentów opcjonalnych.

Argparse automatycznie generuje komunikaty błędów przy nieprawidłowym użyciu i wyświetla pomoc `-h` z czytelnym opisem wszystkich argumentów, co znacząco podnosi użyteczność skryptu dla innych użytkowników. W przypadku skryptów z więcej niż dwoma argumentami, szczególnie gdy niektóre są opcjonalne, `argparse` jest zdecydowanie lepszym wyborem. Dla projektów komercyjnych i skryptów przeznaczonych do dystrybucji zaleca się zawsze używanie `argparse` lub innych zaawansowanych bibliotek, takich jak `click` czy `typer`, które oferują jeszcze więcej możliwości.

53Moduł csv - praca z plikami CSV

Co to jest CSV?

CSV (Comma-Separated Values) to format przechowywania danych tabelarycznych w pliku tekstowym. Każdy wiersz to nowa linia, a kolumny są oddzielone przecinkami.

Moduł csv

Wbudowany moduł do odczytu i zapisu plików CSV:

  • csv.reader() - odczyt
  • csv.writer() - zapis
  • csv.DictReader() - odczyt jako słowniki
  • csv.DictWriter() - zapis ze słowników

Zastosowania

  • Eksport/import danych z Excela
  • Przetwarzanie logów
  • Integracja systemów
import csv

# === ZAPIS DO CSV ===
print("=== Zapis CSV ===")
dane = [
    ["Imię", "Wiek", "Miasto"],
    ["Anna", "20", "Warszawa"],
    ["Bartek", "25", "Kraków"],
    ["Celina", "30", "Gdańsk"]
]

with open("osoby.csv", "w", newline="", encoding="utf-8") as plik:
    writer = csv.writer(plik)
    writer.writerows(dane)
print("Zapisano do osoby.csv")

# === ODCZYT Z CSV ===
print("\n=== Odczyt CSV ===")
with open("osoby.csv", "r", encoding="utf-8") as plik:
    reader = csv.reader(plik)
    for wiersz in reader:
        print(wiersz)

# === ODCZYT JAKO SŁOWNIKI ===
print("\n=== Odczyt jako słowniki ===")
with open("osoby.csv", "r", encoding="utf-8") as plik:
    reader = csv.DictReader(plik)
    for wiersz in reader:
        print(f"{wiersz['Imię']} z {wiersz['Miasto']} ma {wiersz['Wiek']} lat")

# === ZAPIS ZE SŁOWNIKÓW ===
print("\n=== Zapis ze słowników ===")
with open("nowe.csv", "w", newline="", encoding="utf-8") as plik:
    fieldnames = ["Imię", "Wiek", "Miasto"]
    writer = csv.DictWriter(plik, fieldnames=fieldnames)
    writer.writeheader()
    writer.writerow({"Imię": "Adam", "Wiek": "22", "Miasto": "Poznań"})
print("Zapisano do nowe.csv")

Moduł `csv` w Pythonie umożliwia odczyt i zapis danych w formacie CSV, który jest powszechnie używany do wymiany danych między różnymi systemami i aplikacjami. Funkcja `csv.reader()` tworzy obiekt czytnika, który iteruje po wierszach pliku CSV, zwracając każdy wiersz jako listę wartości. Funkcja `csv.writer()` tworzy obiekt pisarza, który zapisuje listy wartości jako wiersze w pliku CSV z odpowiednim formatowaniem. Domyślnym separatorem kolumn jest przecinek, ale można go zmienić za pomocą parametru `delimiter`.

`csv.DictReader()` i `csv.DictWriter()` oferują wygodniejszy interfejs, traktując pierwszy wiersz pliku CSV jako nagłówek z nazwami kolumn i zwracając każdy wiersz jako słownik, gdzie kluczami są nazwy kolumn. Jest to szczególnie przydatne przy przetwarzaniu danych z arkuszami kalkulacyjnymi i eksportem z baz danych. Parametr `quoting` kontroluje sposób obsługi cudzysłowów w wartościach, a `escapechar` definiuje znak ucieczki dla specjalnych znaków w wartościach. Moduł `csv` jest standardowym narzędziem do pracy z danymi tabelarycznymi w Pythonie i powinien być używany zamiast ręcznego parsowania plików CSV.

54Moduł statistics - funkcje statystyczne

Moduł statistics

statistics to wbudowany moduł do obliczeń statystycznych.

Podstawowe funkcje

  • mean(dane) - średnia arytmetyczna
  • median(dane) - mediana
  • mode(dane) - dominanta (najczęstsza wartość)
  • stdev(dane) - odchylenie standardowe
  • variance(dane) - wariancja
  • quantiles(dane, n) - kwantyle

Zastosowania

  • Analiza wyników pomiarów
  • Raporty i statystyki
  • Analiza danych z Excela
import statistics

# Dane przykładowe - wyniki egzaminu
wyniki = [85, 90, 78, 92, 88, 76, 95, 89, 84, 91]

print("=== Analiza wyników egzaminu ===")
print(f"Wyniki: {wyniki}")

srednia = statistics.mean(wyniki)
print(f"Średnia: {srednia:.2f}")

mediana = statistics.median(wyniki)
print(f"Mediana: {mediana}")

odchylenie = statistics.stdev(wyniki)
print(f"Odchylenie standardowe: {odchylenie:.2f}")

wariancja = statistics.variance(wyniki)
print(f"Wariancja: {wariancja:.2f}")

# Trywialne dane
dane_nieparzyste = [1, 2, 3, 4, 5, 6, 7]
print(f"\nDane nieparzyste: {dane_nieparzyste}")
print(f"Średnia: {statistics.mean(dane_nieparzyste)}")
print(f"Mediana: {statistics.median(dane_nieparzyste)}")

# Kwartyle
print("\n=== Kwantyle ===")
kwartyle = statistics.quantiles(wyniki, n=4)
print(f"Kwartyle: {kwartyle}")

# Moda (najczęstsza wartość, zwraca pierwszą napotkaną)
# W Pythonie 3.8+ mode() zwraca pierwszą napotkaną wartość, gdy jest ich kilka
wartosci = [1, 2, 2, 2, 3, 4, 4, 4, 5]
print(f"\nWartości: {wartosci}")
print(f"Moda (najczęstsza): {statistics.mode(wartosci)}")

Moduł `statistics` w Pythonie dostarcza podstawowych funkcji statystycznych do analizy zbiorów danych liczbowych, co jest przydatne przy przetwarzaniu wyników pomiarów, analizie logów i raportowaniu. Funkcja `statistics.mean()` oblicza średnią arytmetyczną, `median()` medianę, a `mode()` dominantę zbioru danych. Do pomiaru rozrzutu danych służą `stdev()` dla odchylenia standardowego i `variance()` dla wariancji, które mogą być obliczane dla próbki lub całej populacji. Funkcja `harmonic_mean()` oblicza średnią harmoniczną, a `geometric_mean()` geometryczną.

Moduł `statistics` oferuje także funkcje do analizy korelacji, takie jak `correlation()` dla współczynnika korelacji Pearsona i `linear_regression()` dla prostej regresji liniowej. W praktyce administracyjnej moduł `statistics` jest mniej popularny niż dedykowane biblioteki, takie jak NumPy i Pandas, które oferują znacznie bardziej zaawansowane możliwości analizy danych. Dla podstawowych obliczeń statystycznych na niewielkich zbiorach danych moduł `statistics` jest jednak w pełni wystarczający i nie wymaga instalowania dodatkowych zależności, co czyni go dobrym wyborem dla prostych skryptów analitycznych.

55Rekursywne przechodzenie katalogów - os.walk

Moduł os.walk

os.walk() pozwala rekursywnie przechodzić przez strukturę katalogów. Jest idealny do wyszukiwania plików, backupu katalogów, analizy struktury.

Jak działa?

Dla każdego katalogu zwraca trójkę (katalog, podkatalogi, pliki).

Zastosowania

  • Wyszukiwanie plików określonego typu
  • Backup całych struktur katalogów
  • Zliczanie plików i katalogów
  • Analiza użycia dysku
import os

# Rekurencyjne przechodzenie katalogów
print("=== Struktura katalogów ===")
for katalog, podkatalogi, pliki in os.walk(".", topdown=True):
    # Poziom zagłębienia
    glebokosc = katalog.count(os.sep)
    wciecie = "  " * glebokosc
    
    print(f"{wciecie}{os.path.basename(katalog) or '.'}/")
    
    # Podkatalogi
    for podkatalog in podkatalogi:
        print(f"{wciecie}  [K] {podkatalog}/")
    
    # Pliki
    for plik in pliki[:5]:  # max 5 plików na katalog
        print(f"{wciecie}  [P] {plik}")
    if len(pliki) > 5:
        print(f"{wciecie}  ... i {len(pliki) - 5} więcej plików")

# Wyszukiwanie plików określonego typu
print("\n=== Wyszukiwanie plików .py ===")
pliki_py = []
for katalog, podkatalogi, pliki in os.walk("."):
    for plik in pliki:
        if plik.endswith(".py"):
            pliki_py.append(os.path.join(katalog, plik))

print(f"Znaleziono {len(pliki_py)} plików .py:")
for p in pliki_py[:10]:
    print(f"  {p}")

Funkcja `os.walk()` w Pythonie umożliwia rekurencyjne przechodzenie przez drzewo katalogów, co jest niezbędne przy przetwarzaniu wszystkich plików w złożonej strukturze katalogów. Funkcja zwraca generator, który dla każdego odwiedzonego katalogu zwraca krotkę zawierającą ścieżkę katalogu (`root`), listę podkatalogów (`dirs`) i listę plików (`files`). Domyślnie `os.walk()` przechodzi przez katalogi od góry do dołu, ale parametr `topdown=False` zmienia kolejność na odwrotną. Modyfikacja listy `dirs` w trakcie iteracji pozwala na kontrolowanie, które podkatalogi zostaną odwiedzone.

W praktyce administracyjnej `os.walk()` jest używany do skryptów sprzątających, które usuwają pliki tymczasowe ze złożonych struktur katalogów, narzędzi do tworzenia kopii zapasowych całych drzew katalogów oraz programów do analizy wykorzystania przestrzeni dyskowej. Alternatywą w nowszych wersjach Pythona jest metoda `Path.rglob()` z modułu `pathlib`, która oferuje podobną funkcjonalność w bardziej obiektowym interfejsie. Przy pracy z bardzo dużymi drzewami katalogów warto rozważyć zastosowanie `os.scandir()` zamiast `os.listdir()` dla lepszej wydajności, szczególnie gdy potrzebne są informacje o typie pliku bez osobnego wywołania `os.path.isdir()`.

56Debugowanie - moduł pdb

Co to jest debugowanie?

Debugowanie to proces znajdowania i naprawiania błędów w kodzie. Moduł pdb (Python Debugger) pozwala na interaktywne śledzenie kodu.

Podstawowe polecenia pdb

  • l (list) - wyświetla kod wokół bieżącej linii
  • n (next) - wykonuje następną linię
  • s (step) - wchodzi do funkcji
  • p zmienna (print) - wyświetla wartość zmiennej
  • c (continue) - kontynuuje do następnego breakpointu
  • q (quit) - kończy debugowanie

Jak włączyć?

Dodaj breakpoint() w kodzie lub uruchom z python -m pdb skrypt.py

# === PROSTY PRZYKŁAD DEBUGOWANIA ===
# Uruchom z: python -m pdb debug_example.py
# Lub dodaj breakpoint() w kodzie

def oblicz_silnie(n):
    """Oblicza silnię (n!)."""
    if n < 0:
        raise ValueError("Silnia nie jest określona dla liczb ujemnych")
    if n == 0 or n == 1:
        return 1
    
    wynik = 1
    for i in range(2, n + 1):
        wynik = wynik * i
    return wynik

def debuguj_silnie():
    """Demonstruje debugowanie."""
    liczby = [5, 3, 7, -1]
    
    for liczba in liczby:
        print(f"Obliczam silnię z {liczba}...")
        # breakpoint()  # <-- Odkomentuj aby debugować
        try:
            wynik = oblicz_silnie(liczba)
            print(f"  {liczba}! = {wynik}")
        except ValueError as e:
            print(f"  Błąd: {e}")

# Uruchomienie
if __name__ == "__main__":
    debuguj_silnie()

# === WSKAZÓWKI DEBUGOWANIA ===
# 1. Uruchom: python -m pdb skrypt.py
# 2. (Pdb) l  - wyświetl kod
# 3. (Pdb) n  - następna linia
# 4. (Pdb) p zmienna - pokaż zmienną
# 5. (Pdb) c  - kontynuuj
# 6. (Pdb) q  - wyjdź

Moduł `pdb` (Python Debugger) w Pythonie jest wbudowanym narzędziem do debugowania kodu, które pozwala na zatrzymanie wykonania programu w dowolnym momencie i interaktywną inspekcję stanu zmiennych. Najprostszym sposobem użycia `pdb` jest wstawienie w kodzie linii `import pdb; pdb.set_trace()`, która zatrzymuje program w tym miejscu i otwiera konsolę debuggera. W Pythonie 3.7 wprowadzono wygodniejszą funkcję wbudowaną `breakpoint()`, która robi to samo i może być skonfigurowana do używania różnych debuggerów.

W konsoli `pdb` dostępne są podstawowe komendy: `n` (next) do wykonania następnej linii, `s` (step) do wejścia do funkcji, `c` (continue) do kontynuowania do następnego breakpointa, `p zmienna` (print) do wyświetlenia wartości zmiennej oraz `l` (list) do pokazania otaczającego kodu. Debugger pozwala także na modyfikowanie wartości zmiennych w czasie rzeczywistym i wykonywanie dowolnych wyrażeń Pythonowych. Mimo że nowoczesne IDE oferują zaawansowane graficzne debugowanie, znajomość `pdb` jest przydatna podczas debugowania skryptów na serwerach bez dostępu do interfejsu graficznego.

57Testowanie - wprowadzenie do pytest

Dlaczego testować?

Testy automatyczne sprawdzają, czy kod działa poprawnie. Oszczędzają czas i zapobiegają błędom.

pytest

pytest to popularna biblioteka do testowania w Pythonie. Instalacja: pip install pytest

Zasady pisania testów

  • Każda funkcja testowa zaczyna się od test_
  • Używaj assert do sprawdzania warunków
  • Testy powinny być niezależne od siebie
  • Nazywaj testy opisowo
# === PLIK: kalkulator.py ===

def dodaj(a, b):
    return a + b

def odejmij(a, b):
    return a - b

def pomnóż(a, b):
    return a * b

def podziel(a, b):
    if b == 0:
        raise ZeroDivisionError("Nie można dzielić przez zero!")
    return a / b

# === PLIK: test_kalkulator.py ===

import pytest
from kalkulator import dodaj, odejmij, pomnóż, podziel

def test_dodaj():
    assert dodaj(2, 3) == 5
    assert dodaj(-1, 1) == 0
    assert dodaj(0, 0) == 0

def test_odejmij():
    assert odejmij(5, 3) == 2
    assert odejmij(3, 5) == -2

def test_pomnóż():
    assert pomnóż(3, 4) == 12
    assert pomnóż(0, 100) == 0

def test_podziel():
    assert podziel(10, 2) == 5
    assert podziel(9, 3) == 3

def test_podziel_przez_zero():
    with pytest.raises(ZeroDivisionError):
        podziel(10, 0)

# Uruchomienie testów:
# pytest test_kalkulator.py -v

Testowanie kodu w Pythonie za pomocą frameworka `pytest` jest standardową praktyką w profesjonalnych projektach, która pozwala na automatyczną weryfikację poprawności działania programu. Podstawowa zasada `pytest` polega na pisaniu funkcji testowych w osobnych plikach, które zawierają asercje sprawdzające oczekiwane zachowanie testowanych funkcji. Aby uruchomić testy, wystarczy wpisać w terminalu polecenie `pytest`, które automatycznie znajdzie wszystkie pliki zawierające testy i wykona je, raportując wyniki. Funkcje testowe muszą zaczynać się od `test_` lub kończyć na `_test`, aby `pytest` mógł je zidentyfikować.

Pytest oferuje zaawansowane funkcje, takie jak `fixtures` do przygotowywania danych testowych, `parametrize` do uruchamiania tego samego testu z różnymi danymi oraz `mocking` do izolowania testów od zewnętrznych zależności. Asercje w pytest są czytelniejsze niż standardowe `assert` z Pythonem, a w przypadku niepowodzenia testu framework szczegółowo informuje, jaka wartość była oczekiwana, a jaka została faktycznie zwrócona. Systematyczne testowanie kodu jest kluczowym elementem profesjonalnego procesu wytwarzania oprogramowania i powinno być stosowane nawet w przypadku prostych skryptów administracyjnych.

58Sztuczki i triki w Pythonie

Przydatne sztuczki

Oto kilka przydatnych technik, które ułatwiają życie w Pythonie.

Zamiana zmiennych

Bez zmiennej tymczasowej!

Rozpakowanie krotki

Jednoczesne przypisanie wielu wartości.

Operator trójskładnikowy

Skrócony if-else w jednej linii.

Odwrócenie listy i stringa

[::-1]

# === ZAMIANA ZMIENNYCH BEZ TYMCZASOWEJ ===
print("=== Zamiana zmiennych ===")
a, b = 5, 10
print(f"Przed: a={a}, b={b}")
a, b = b, a
print(f"Po: a={a}, b={b}")

# === OPERATOR TRÓJSKLADNIKOWY ===
print("\n=== Operator trójskładnikowy ===")
wiek = 20
status = "pełnoletni" if wiek >= 18 else "niepełnoletni"
print(f"Status: {status}")

# === ROZPAKOWANIE KROTKILISTY ===
print("\n=== Rozpakowanie ===")
pierwszy, *reszta, ostatni = [1, 2, 3, 4, 5]
print(f"Pierwszy: {pierwszy}, Reszta: {reszta}, Ostatni: {ostatni}")

# === ODWRÓCENIE ===
print("\n=== Odwracanie ===")
lista = [1, 2, 3, 4, 5]
print(f"Odwrócona lista: {lista[::-1]}")
tekst = "Python"
print(f"Odwrócony tekst: {tekst[::-1]}")

# === ANY i ALL ===
print("\n=== any() i all() ===")
liczby = [2, 4, 6, 8]
print(f"Wszystkie parzyste? {all(x % 2 == 0 for x in liczby)}")
print(f"Co najmniej jedna parzysta? {any(x % 2 == 0 for x in liczby)}")

# === ENUMERATE z wieloma listami ===
print("\n=== Zip i enumerate ===")
imiona = ["Anna", "Bartek", "Celina"]
wieki = [20, 25, 30]
for i, (imie, wiek) in enumerate(zip(imiona, wieki)):
    print(f"{i+1}. {imie} ma {wiek} lat")

Python oferuje wiele sztuczek i trików, które pozwalają na pisanie bardziej zwięzłego i wydajnego kodu. Operator `:=` (walrus operator) wprowadzony w Pythonie 3.8 pozwala na przypisanie wartości do zmiennej w wyrażeniu, co jest przydatne w pętlach i warunkach, na przykład `while (linia := plik.readline()):`. Funkcja `zip()` łączy równolegle kilka kolekcji, zwracając iterator krotek, co jest przydatne przy iteracji po kilku listach jednocześnie. Funkcja `enumerate()` dodaje licznik do iteracji, zwracając pary (indeks, element).

Kolekcje domyślne, takie jak `setdefault()` dla słowników czy `defaultdict` z modułu `collections`, eliminują konieczność ręcznego sprawdzania istnienia kluczy przed odczytem. Wyrażenia warunkowe (ternary operator) w formie `wartosc if warunek else inna_wartosc` pozwalają na zwięzłe przypisania warunkowe w jednej linii. Dekoratory z symbolem `@` umożliwiają modyfikację zachowania funkcji bez zmiany ich kodu, co jest szeroko stosowane w frameworkach internetowych. Znajomość tych i innych zaawansowanych konstrukcji językowych pozwala na pisanie bardziej eleganckiego i wydajnego kodu w Pythonie.

59Środowiska wirtualne - venv

Co to jest środowisko wirtualne?

Środowisko wirtualne (venv) to izolowana przestrzeń dla projektu Python. Każdy projekt może mieć własne wersje pakietów.

Dlaczego używać?

  • Unikanie konfliktów między projektami
  • Czystość - tylko potrzebne pakiety
  • Łatwe usuwanie projektu
  • Współdzielenie kodu między zespołem

Kroki użycia

  1. Utwórz: python -m venv .venv
  2. Aktywuj: source .venv/bin/activate (Linux) lub .venv\Scripts\activate (Windows)
  3. Instaluj pakiety: pip install nazwa
  4. Zapisz zależności: pip freeze > requirements.txt
# === TWORZENIE ŚRODOWISKA WIRTUALNEGO ===

# Linux/macOS
$ python3 -m venv .venv

# Windows
C:\> python -m venv .venv

# === AKTYWACJA ===

# Linux/macOS
$ source .venv/bin/activate
(.venv) $

# Windows PowerShell
PS C:\> .\.venv\Scripts\Activate.ps1
(.venv) PS C:\>

# Windows CMD
C:\> .\.venv\Scripts\activate.bat
(.venv) C:\>

# === INSTALACJA PAKIETÓW ===

(.venv) $ pip install requests
(.venv) $ pip install pytest

# === ZAPISYWANIE ZALEŻNOŚCI ===

(.venv) $ pip freeze > requirements.txt

# Plik requirements.txt wygląda tak:
# requests==2.31.0
# pytest==7.4.0

# === INSTALACJA Z PLIKU ===

(.venv) $ pip install -r requirements.txt

# === DEZAKTYWACJA ===

(.venv) $ deactivate
$

Środowiska wirtualne w Pythonie, tworzone za pomocą modułu `venv`, są podstawowym narzędziem do izolowania zależności między projektami i zapobiegania konfliktom wersji bibliotek. Utworzenie środowiska wirtualnego wykonuje się poleceniem `python -m venv nazwa_srodowiska`, które tworzy katalog z niezależną instalacją Pythona i własnym menedżerem pakietów `pip`. Aktywacja środowiska w systemie Windows odbywa się przez uruchomienie `nazwa_srodowiska\Scripts\activate`, a w systemach Linux i macOS przez `source nazwa_srodowiska/bin/activate`. Po aktywacji wszystkie pakiety instalowane za pomocą `pip` trafiają tylko do tego środowiska, nie wpływając na systemową instalację Pythona.

Po zakończeniu pracy środowisko dezaktywuje się poleceniem `deactivate`, które przywraca oryginalne ustawienia ścieżek. Zaleca się używanie osobnego środowiska wirtualnego dla każdego projektu i dołączanie pliku `requirements.txt` z listą zależności, który można wygenerować za pomocą `pip freeze > requirements.txt` i odtworzyć za pomocą `pip install -r requirements.txt`. Nowoczesną alternatywą dla `venv` jest `Poetry` lub `Pipenv`, które oferują bardziej zaawansowane zarządzanie zależnościami i budowanie pakietów. Środowiska wirtualne są standardem w profesjonalnych projektach Pythonowych i powinny być używane od samego początku nauki.

60Podsumowanie i następne kroki

Czego się nauczyliśmy?

  • Podstawy Pythona - zmienne, typy, operacje
  • Funkcje - tworzenie, parametry, return
  • Struktury danych - listy, słowniki, zbiory
  • Instrukcje warunkowe i pętle
  • Obsługa plików i katalogów
  • Obsługa błędów
  • Moduły standardowe
  • Argumenty wiersza poleceń

Następne kroki

  • Praktykuj! - Pisz własne skrypty
  • Pakiety zewnętrzne - requests, numpy, pandas
  • Automatyzacja IT - paramiko (SSH), ansible
  • API - komunikacja z serwisami webowymi
  • Testowanie - pytest, unittest

Dobre praktyki

  • Pisz czytelny kod z komentarzami
  • Używaj virtualenv dla każdego projektu
  • Testuj swój kod
  • Ucz się na błędach
+----------------------------------+
|      GRATULACJE!                |
|                                  |
|  Ukończyłeś kurs podstaw Pythona |
|                                  |
|  Teraz możesz:                   |
|  [x] Pisać własne skrypty        |
|  [x] Automatyzować zadania       |
|  [x] Przetwarzać pliki           |
|  [x] Analizować dane             |
|                                  |
|  Powodzenia w dalszej nauce!     |
|                                  |
+----------------------------------+
            

Podsumowanie kursu Pythona zestawia wszystkie kluczowe zagadnienia, które zostały omówione w trakcie nauki, od podstaw składni i typów danych po zaawansowane moduły i narzędzia programistyczne. Uczestnik kursu poznał zmienne, instrukcje warunkowe, pętle i funkcje, które stanowią fundament każdego języka programowania. Praktyczne moduły, takie jak `os`, `pathlib`, `json`, `subprocess` i `argparse`, przygotowują do tworzenia rzeczywistych skryptów automatyzujących zadania administracyjne i przetwarzających dane. Obsługa błędów, debugowanie i testowanie to kluczowe umiejętności, które odróżniają początkujących programistów od profesjonalistów.

Po ukończeniu kursu warto kontynuować naukę poprzez realizację własnych projektów, które pozwolą na praktyczne zastosowanie zdobytej wiedzy. Dokumentacja oficjalna Pythona dostępna na stronie docs.python.org jest wyczerpującym źródłem wiedzy, a platformy takie jak Stack Overflow czy Reddit oferują pomoc społeczności w przypadku napotkania problemów. Polecane kierunki dalszego rozwoju to między innymi frameworki webowe Django i Flask, analiza danych z Pandas i NumPy, automatyzacja z Ansible, oraz programowanie aplikacji desktopowych z PyQt. Systematyczna praktyka i stopniowe zwiększanie poziomu trudności projektów to klucz do osiągnięcia biegłości w Pythonie.