Skip to content

Latest commit

 

History

History
5397 lines (3541 loc) · 139 KB

File metadata and controls

5397 lines (3541 loc) · 139 KB

Read in other languages: English 🇺🇸, Polski 🇵🇱, German 🇩🇪, French 🇫🇷, Spanish 🇪🇸, Ukraiński 🇺🇦.

Python Python logo

Najpopularniejsze pytania i odpowiedzi rekrutacyjne z Pythona

1. Czym jest Python i jakie są jego główne cechy?

Python

Python to wysokopoziomowy język ogólnego przeznaczenia, nastawiony na czytelność, szybki rozwój i rozbudowany standardowy zestaw narzędzi.

Główne cechy:

  • Prosta składnia: kod łatwo czytać i utrzymywać.
  • Interpretowany model wykonania: szybki cykl zmian bez ręcznego etapu kompilacji.
  • Wieloplatformowość: ten sam kod działa na Linuxie, macOS i Windowsie.
  • Wieloparadygmatowość: podejścia proceduralne, OOP i funkcyjne w jednym projekcie.
  • Silny ekosystem: pip, venv, pyproject.toml, tysiące gotowych bibliotek.
  • Nowoczesne możliwości Pythona 3.10+: type hints, dataclass, async/await, match/case, generatory, menedżery kontekstu.

Przykład funkcji w stylu production:

from dataclasses import dataclass

@dataclass(slots=True)
class User:
    name: str
    active: bool

def process_users(users: list[User]) -> list[str]:
    return [user.name for user in users if user.active]

W skrócie:

  • Python przyspiesza rozwój dzięki prostej składni i dużej bibliotece standardowej.
  • Język nadaje się do backendu, automatyzacji, data/ML, testowania i DevOps.
  • W Pythonie 3.10+ kluczowe praktyki to adnotacje typów, async I/O i nowoczesna biblioteka standardowa.
2. Jakie są kluczowe zalety Pythona?

Python

Kluczowe zalety Pythona w production:

  • wysoka szybkość rozwoju dzięki zwięzłej składni;
  • duża biblioteka standardowa (pathlib, itertools, collections, asyncio);
  • dojrzały ekosystem pakietów dla backendu, data, automatyzacji i testowania;
  • przenośność kodu między systemami operacyjnymi;
  • dobre wsparcie dla typowania (typing, mypy, pyright) i nowoczesnych praktyk.

W skrócie:

  • Python skraca time-to-market.
  • Daje wiele gotowych narzędzi bez dodatkowych zależności.
  • Nadaje się zarówno do MVP, jak i do skalowalnych serwisów.
3. Czym Python różni się od języków kompilowanych?

Python

Python zwykle jest wykonywany przez interpreter: kod jest kompilowany do bajtkodu i uruchamiany w czasie wykonania (CPython VM). W językach kompilowanych (C/C++, Rust) zazwyczaj występuje wcześniejsza kompilacja do kodu maszynowego.

Praktyczne konsekwencje:

  • Python jest szybszy w rozwoju i prototypowaniu.
  • Natywnie kompilowane języki są zwykle szybsze w zadaniach CPU-bound.
  • W Pythonie wydajność często poprawia się algorytmami, profilowaniem i rozszerzeniami C.

W skrócie:

  • Python optymalizuje szybkość rozwoju.
  • Kompilacja do kodu maszynowego zwykle daje lepszą surową wydajność.
  • Wybór zależy od domeny i wymagań dotyczących opóźnień oraz przepustowości.
4. Co oznacza dynamiczne typowanie w Pythonie?

Python

Dynamiczne typowanie oznacza, że typ jest przypisany do obiektu, a nie do nazwy zmiennej. Nazwa może wskazywać na wartości różnych typów w różnym czasie wykonania.

value = 10        # int
value = "10"      # str

Typy są sprawdzane podczas wykonania, dlatego błędy typów pojawiają się w w czasie wykonania, jeśli nie używa się statycznego analizatora typów.

W skrócie:

  • W Pythonie typ ma obiekt, a nie zmienna.
  • Typ może się zmieniać przy ponownym przypisaniu nazwy.
  • Type hints dodają kontrolę przed uruchomieniem kodu.
5. Co oznacza strong typing w Pythonie?

Python

Strong typing w Pythonie oznacza, że interpreter nie wykonuje niebezpiecznych, niejawnych konwersji między niezgodnymi typami.

1 + "2"  # TypeError

Dla operacji na różnych typach potrzebne jest jawne rzutowanie:

1 + int("2")  # 3

W skrócie:

  • Python jest dynamicznie typowany, ale rygorystyczny pod względem zgodności typów.
  • Niebezpieczne niejawne konwersje są blokowane błędem.
  • Jawne rzutowanie czyni zachowanie przewidywalnym.
6. Czym jest REPL i kiedy się go używa?

Python

REPL (Read-Eval-Print Loop) to tryb interaktywny, w którym wpisujesz wyrażenie, natychmiast otrzymujesz wynik i szybko weryfikujesz hipotezy.

Zastosowania:

  • sprawdzenie API biblioteki;
  • szybkie przetestowanie wyrażenia lub algorytmu;
  • zbadanie danych przed implementacją w module.

Narzędzia: standardowy python, ipython, ptpython.

W skrócie:

  • REPL daje najszybszą pętlę feedbacku.
  • Jest wygodny do eksperymentów i debugowania.
  • Nie zastępuje testów, ale skraca czas sprawdzania pomysłów.
7. Czym są literały w Pythonie? Podaj przykłady różnych typów literałów.

Python

Literał to stała wartość zapisana bezpośrednio w kodzie.

name = "Ada"                  # str literal
count = 42                    # int literal
ratio = 3.14                  # float literal
enabled = True                # bool literal
items = [1, 2, 3]             # list literal
config = {"retries": 3}       # dict literal
flags = {"a", "b"}            # set literal
point = (10, 20)              # tuple literal
raw = b"abc"                  # bytes literal

W skrócie:

  • Literały to wbudowane stałe wartości w kodzie.
  • Każdy podstawowy typ ma własną składnię literału.
  • Są często używane do inicjalizacji danych.
8. Czym są keywords w Pythonie?

Python

Keywords to zarezerwowane słowa języka, które mają specjalne znaczenie składniowe i nie mogą być nazwami zmiennych.

Przykłady: if, for, class, def, match, case, try, except, async, await.

Listę można sprawdzić:

import keyword
print(keyword.kwlist)

W skrócie:

  • Keywords tworzą składnię Pythona.
  • Nie można ich używać jako identyfikatorów.
  • Lista jest dostępna przez moduł keyword.
9. Czym jest zmienna w Pythonie?

Python

Zmienna w Pythonie to nazwa (reference), która wskazuje na obiekt w pamięci. Przypisanie wiąże nazwę z obiektem, a nie „wkłada wartość do pudełka”.

x = [1, 2]
y = x
y.append(3)
# x i y wskazują na tę samą listę

W skrócie:

  • Zmienna jest referencją do obiektu.
  • Kilka nazw może wskazywać na ten sam mutable object.
  • To ważne dla zrozumienia kopiowania i efektów ubocznych.
10. Czym są `pass` i `...` (Ellipsis) w Pythonie?

Python

pass to instrukcja-zagłuszka: nic nie robi, ale pozwala zapisać poprawny składniowo pusty blok.

... (Ellipsis) to osobny obiekt Ellipsis. Często jest używany jako znacznik „jeszcze niezaimplementowane”, w type hints i bibliotekach, na przykład NumPy.

def feature() -> None:
    pass

PLACEHOLDER = ...

W skrócie:

  • pass jest przydatny w pustych blokach kodu.
  • ... jest wartością-obiektem, a nie słowem kluczowym.
  • Oba są używane jako tymczasowe zagłuszki, ale mają różną semantykę.
11. Czym jest PEP i jak wpływa na rozwój Pythona?

Python

PEP (Python Enhancement Proposal) to oficjalny dokument opisujący nową funkcję, standard lub proces w ekosystemie Pythona.

Przez PEP przechodzą:

  • dyskusja nad projektem;
  • argumentacja techniczna;
  • decyzja o przyjęciu lub odrzuceniu;
  • standaryzacja praktyk.

Przykłady: PEP 8 (styl), PEP 484 (type hints), PEP 634 (pattern matching).

W skrócie:

  • PEP to mechanizm ewolucji języka i narzędzi.
  • Sprawia, że zmiany są przejrzyste i sformalizowane.
  • Wiele współczesnych praktyk w Pythonie zostało utrwalonych właśnie przez PEP.
12. Czym jest PEP 8 i po co jest potrzebny?

Python

PEP 8 to oficjalny przewodnik po stylu kodu Pythona: nazewnictwo, formatowanie, wcięcia, importy, długość linii i czytelność konstrukcji.

Dlaczego jest potrzebny:

  • kod ma jednolity styl w zespole;
  • code review przebiega szybciej;
  • jest mniej szumu w diffie;
  • rośnie maintainability.

W praktyce styl automatyzuje się przez ruff format albo black + ruff.

W skrócie:

  • PEP 8 standaryzuje styl kodu Pythona.
  • Chodzi o czytelność i utrzymanie, a nie o wydajność.
  • Przestrzeganie zasad najlepiej automatyzować narzędziami.
13. Jakie nowe możliwości pojawiły się we współczesnych wersjach Pythona (3.10+)?

Python

Kluczowe możliwości współczesnego Pythona:

  • match/case (structural pattern matching);
  • ulepszona składnia i komunikaty o błędach;
  • typy union przez | (int | None);
  • typing.Self, typing.TypeAlias, typing.override;
  • generics w stylu PEP 695 (class Box[T]: ..., def f[T](...) -> ...);
  • tomllib w bibliotece standardowej.

W praktyce zmniejsza to boilerplate i poprawia niezawodność typowanego kodu.

W skrócie:

  • Python 3.10+ znacząco poprawił składnię i typing.
  • match/case i nowoczesny typing mają najbardziej zauważalny wpływ na kod.
  • Nowych funkcji warto domyślnie używać w nowym kodzie.
14. Czym jest docstring w Pythonie?

Python

Docstring to łańcuch dokumentacyjny, pierwszy wyrażenie w module, klasie lub funkcji. Jest dostępny przez __doc__ i używany przez IDE, help() oraz generatory dokumentacji.

def normalize_email(email: str) -> str:
    """Return lowercase email without surrounding spaces."""
    return email.strip().lower()

Zaleca się opisywać: co robi funkcja, argumenty, wartość zwracaną i wyjątki.

W skrócie:

  • Docstring to wbudowana dokumentacja w kodzie.
  • Działa jako źródło dla help() i automatycznego generowania dokumentacji.
  • Dobry docstring zmniejsza potrzebę czytania implementacji.
15. Jak działają wcięcia (indentation) w Pythonie?

Python

W Pythonie wcięcia definiują bloki kodu (ciało if, for, def, class). Oznacza to, że wcięcie jest częścią składni, a nie tylko stylem.

if is_ready:
    run_task()
else:
    stop_task()

Mieszanie tabów i spacji może prowadzić do IndentationError albo niepoprawnej struktury bloku.

W skrócie:

  • Wcięcie w Pythonie definiuje strukturę programu.
  • Nieprawidłowe wcięcie psuje wykonanie.
  • Używaj spójnego stylu, najlepiej 4 spacji.
16. Ile spacji należy stosować do wcięcia zgodnie z PEP 8 i dlaczego ważne jest zachowanie jednolitych wcięć?

Python

PEP 8 zaleca 4 spacje na jeden poziom wcięcia.

Dlaczego to istotne:

  • jednolita struktura bloków w całym projekcie;
  • przewidywalny wygląd w różnych edytorach;
  • mniej błędów przez konflikty tab/spacja;
  • czytelniejszy diff w Git.

W skrócie:

  • Standardowe wcięcie to 4 spacje.
  • Jeden styl eliminuje całą klasę błędów formatowania.
  • Bezpośrednio poprawia to czytelność i utrzymanie kodu.
17. Jaka jest różnica między `ruff` a `black` pod względem funkcjonalności i użycia?

Python

black to formatter kodu o stałych regułach.

ruff to szybki linter, a także formatter i auto-fixer wielu reguł (PEP 8, importy, potencjalne bugi, uproszczenia składni).

Praktyczne podejścia:

  • albo ruff check --fix + ruff format;
  • albo ruff do lintu + black do formatowania.

W skrócie:

  • black koncentruje się na formatowaniu.
  • ruff pokrywa linting i część automatycznych poprawek.
  • W nowoczesnych projektach często wystarcza sam ruff.
18. Wyjaśnij przeznaczenie type annotations w Pythonie i podaj przykład funkcji z type annotations.

Python

Type annotations opisują oczekiwane typy argumentów i wartości zwracanej. Poprawiają autouzupełnianie, analizę statyczną i czytelność API.

def process_users(users: list[dict[str, object]]) -> list[str]:
    return [str(user["name"]) for user in users if bool(user.get("active"))]

Adnotacje same w sobie nie wykonują walidacji w czasie działania, ale pomagają wykrywać błędy na etapie CI przez mypy lub pyright.

W skrócie:

  • Type hints dokumentują kontrakt funkcji.
  • Dają wczesne wykrywanie błędów przez static checking.
  • Podnoszą jakość API w dużych codebase'ach.
19. Czym jest debugging i dlaczego to ważna umiejętność dla programistów?

Python

Debugging to systematyczne szukanie przyczyny niepoprawnego zachowania kodu i jej usuwanie. To nie tylko „znaleźć buga”, ale odtworzyć go, zlokalizować i zweryfikować fix.

Podstawowy cykl:

  • odtworzyć problem;
  • zawęzić obszar kodu;
  • sprawdzić hipotezę logami lub debuggerem;
  • dodać test, który utrwala scenariusz.

W skrócie:

  • Debugging zmniejsza MTTR i liczbę regresji.
  • Działa najlepiej jako proces, a nie chaotyczne szukanie.
  • Zakończenie debugowania to: fix + test + dodatkowa weryfikacja.
20. Wyjaśnij, do czego służy używanie funkcji `print` do debugowania. Podaj przykład sytuacji, w której to podejście jest przydatne.

Python

print-debugging to szybki sposób na sprawdzenie wartości zmiennych i kolejności wykonania bez uruchamiania bardziej złożonych narzędzi.

Jest przydatny, gdy trzeba szybko sprawdzić hipotezę w lokalnym skrypcie:

def parse_port(raw: str) -> int:
    value = raw.strip()
    print(f"raw={raw!r} value={value!r}")
    return int(value)

Dla kodu produkcyjnego lepiej przejść na logging, aby mieć poziomy logów i kontrolowane wyjście.

W skrócie:

  • print nadaje się do krótkiej, lokalnej diagnostyki.
  • Szybko pokazuje stan zmiennych w problematycznym miejscu.
  • Do stabilnej diagnostyki w serwisie używaj logging.
21. Opisz, jak można używać Python Debugger (PDB) do debugowania. Jakie zalety ma PDB w porównaniu z `print`?

Python

pdb pozwala zatrzymywać wykonanie programu, przechodzić po liniach, sprawdzać stan zmiennych i stos wywołań.

Podstawowe użycie:

import pdb

def compute(x: int) -> int:
    pdb.set_trace()
    return x * 2

Kluczowe komendy: n (next), s (step), c (continue), p expr (print expr), l (list), bt (backtrace).

W skrócie:

  • pdb daje interaktywną kontrolę nad wykonaniem.
  • Jest skuteczniejszy niż print w bardziej złożonych scenariuszach.
  • Pozwala diagnozować stan bez zaśmiecania kodu logami.
22. Jakie strategie można stosować do debugowania pętli i funkcji w Pythonie?

Python

Praktyczne strategie:

  • zlokalizować błąd przez minimalny odtwarzalny przykład;
  • logować niezmienniki podczas iteracji pętli;
  • ustawiać breakpoint na wejściu i wyjściu z funkcji;
  • sprawdzać przypadki brzegowe (puste dane, None, duże wolumeny);
  • pokrywać problematyczną ścieżkę testem.

Dla pętli warto logować indeks i kluczowe stany pośrednie.

W skrócie:

  • Zaczynaj od odtworzenia i zawężenia problemu.
  • Sprawdzaj niezmienniki i warunki brzegowe.
  • Kończ fixem i testem, który łapie regresję.
23. Jaki wpływ mają długotrwałe funkcje na wydajność programu?

Python

Długotrwałe funkcje zwiększają latency, blokują workery lub wątki i pogarszają przepustowość serwisu.

Ryzyka:

  • wolne odpowiedzi API;
  • timeouty;
  • wzrost kolejek zadań;
  • gorsze wykorzystanie CPU i I/O.

Podejście: profilować (cProfile), rozbijać na mniejsze kroki, używać generatorów i przetwarzania strumieniowego, wynosić ciężkie obliczenia do multiprocessing albo do tła.

W skrócie:

  • Długie funkcje bezpośrednio uderzają w latency.
  • Optymalizację opiera się na profilowaniu, a nie na intuicji.
  • Architektonicznie lepiej rozdzielać pracę CPU-bound i I/O-bound.
24. Czym jest logging i dlaczego lepiej używać go zamiast `print`?

Python

logging to standardowy mechanizm rejestrowania zdarzeń z poziomami (DEBUG, INFO, WARNING, ERROR, CRITICAL), formatami i handlerami (console, file).

Dlaczego jest lepszy od print:

  • kontrolowane poziomy szczegółowości;
  • ustrukturyzowany format;
  • scentralizowana konfiguracja;
  • możliwość integracji ze stackiem observability.

W skrócie:

  • logging nadaje się do produkcji, print nie.
  • Poziomy logów dają kontrolę nad szumem.
  • Logi powinny być ustrukturyzowane i spójne.
25. Czym jest traceback?

Python

Traceback to stos wywołań, który Python pokazuje przy wyjątku: gdzie wykonanie się zaczęło, przez jakie funkcje przeszedł kod i gdzie dokładnie wystąpił błąd.

Zastosowanie:

  • szybko znaleźć plik i linię źródła błędu;
  • zrozumieć ścieżkę wykonania do awarii;
  • zbudować dokładny test odtwarzający problem.

W skrócie:

  • Traceback to „trasa” do błędu.
  • Najcenniejsza część to ostatnie klatki stosu i typ wyjątku.
  • Analiza tracebacku przyspiesza znalezienie root cause.
26. Jakie podstawowe typy danych istnieją w Pythonie?

Python

Podstawowe typy built-in:

  • liczbowe: int, float, complex;
  • logiczny: bool;
  • tekstowe i bajtowe: str, bytes, bytearray;
  • kolekcje: list, tuple, set, dict, frozenset;
  • specjalne: NoneType (None).

Ważne jest rozumienie mutability:

  • mutable: list, dict, set, bytearray;
  • immutable: int, str, tuple, frozenset.

W skrócie:

  • Python ma bogaty zestaw podstawowych typów „out of the box”.
  • Wybór struktury danych wpływa na złożoność operacji.
  • Mutability określa zachowanie kopiowania i efekty uboczne.
27. Jaka jest różnica między zwykłym dzieleniem (`/`) a dzieleniem całkowitym (`//`) w Pythonie?

Python

/ wykonuje zwykłe dzielenie i zawsze zwraca float.

// wykonuje dzielenie floor: zwraca część całkowitą zaokrągloną w dół (do -∞), a typem przy całkowitych operandach jest zwykle int.

7 / 2    # 3.5
7 // 2   # 3
-7 // 2  # -4

W skrócie:

  • / -> wynik rzeczywisty.
  • // -> zaokrąglenie w dół do liczby całkowitej.
  • Dla liczb ujemnych // nie jest równoważne „ucięciu do zera”.
28. Wyjaśnij użycie operatora modulo (`%`) w Pythonie. Jak można go użyć, aby określić, czy liczba jest parzysta czy nieparzysta?

Python

Operator % zwraca resztę z dzielenia.

Sprawdzenie parzystości:

def is_even(n: int) -> bool:
    return n % 2 == 0

Jeśli n % 2 == 0, liczba jest parzysta, w przeciwnym razie nieparzysta.

Typowe zadania: indeksy cykliczne, podział na grupy, obliczenia kalendarzowe.

W skrócie:

  • % zwraca remainder.
  • n % 2 to standardowy test parzystości.
  • Często jest używany do logiki cyklicznej.
29. Jak Python pracuje z dużymi liczbami całkowitymi i jakie są ograniczenia przy pracy z bardzo dużymi liczbami?

Python

int w Pythonie ma dowolną precyzję (arbitrary precision), czyli nie jest ograniczony do 32 lub 64 bitów jak w wielu innych językach.

Ograniczenia są praktyczne:

  • pamięć procesu;
  • czas obliczeń dla bardzo dużych wartości;
  • koszt operacji rośnie wraz ze wzrostem liczby cyfr.

W skrócie:

  • Nie ma klasycznego przepełnienia int.
  • Ograniczeniem są zasoby maszyny, a nie stały rozmiar bitowy.
  • Przy dużych obliczeniach kluczowe są algorytmy i profilowanie.
30. Jakie znaczenie ma obsługa dzielenia przez zero w Pythonie i jak zapobiec `ZeroDivisionError` w kodzie?

Python

Dzielenie przez zero powoduje ZeroDivisionError. Trzeba to obsługiwać jawnie, jeśli dzielnik pochodzi z wejścia zewnętrznego albo jest obliczany dynamicznie.

def safe_div(a: float, b: float) -> float | None:
    if b == 0:
        return None
    return a / b

W krytycznych scenariuszach używaj try/except oraz logowania kontekstu.

W skrócie:

  • Dzielenie przez zero to kontrolowany błąd czasu wykonania.
  • Najprostsza ochrona to sprawdzenie dzielnika przed operacją.
  • Dla danych zewnętrznych dodawaj zabezpieczenia i diagnostykę.
31. Opisz użycie modułu `random` w Pythonie. Jakie są najczęściej używane funkcje tego modułu?

Python

random jest używany do pseudolosowych wartości w symulacjach, danych testowych i zadaniach non-crypto.

Popularne funkcje:

  • random() — liczba z przedziału [0.0, 1.0);
  • randint(a, b) — liczba całkowita z zakresu;
  • randrange(start, stop, step) — jak range, ale losowy element;
  • choice(seq) / choices(seq, k=...);
  • shuffle(list_) — tasuje listę in-place;
  • sample(population, k) — unikalna próbka.

Dla kryptografii używaj secrets, a nie random.

W skrócie:

  • random nadaje się do zwykłej, aplikacyjnej losowości.
  • Najczęściej używa się randint, choice, shuffle, sample.
  • Do zadań security-sensitive potrzebny jest secrets.
32. Jak pracować w Pythonie z liczbami o lepszej precyzji?

Python

Dla finansowych i dokładnych obliczeń dziesiętnych używaj decimal.Decimal, a nie float.

from decimal import Decimal

total = Decimal("0.1") + Decimal("0.2")  # Decimal('0.3')

Dla liczb wymiernych przydatny jest fractions.Fraction.

W skrócie:

  • float jest wygodny, ale ma błędy reprezentacji binarnej.
  • Do dokładnej arytmetyki dziesiętnej wybieraj Decimal.
  • Typ liczby powinien odpowiadać domenie problemu.
33. Czym są znaki escape w stringach Pythona i jak się ich używa? Podaj przykłady dla `\n`, `\t`, `\r`, `\"` i `\'`.

Python

Sekwencje escape to specjalne kombinacje z \, które kodują znaki specjalne wewnątrz stringa.

  • \n — nowa linia
  • \t — tabulacja
  • \r — powrót karetki
  • \" — cudzysłów w stringu z "
  • \' — apostrof w stringu z '
text = "A\tB\n\"quoted\"\nI\'m here\rX"

W skrócie:

  • Znaki escape sterują formatowaniem stringa.
  • Pozwalają wstawiać cudzysłowy bez łamania składni.
  • Dla ścieżek i regexów często wygodnie używać raw stringów r"...".
34. Wyjaśnij użycie metod `strip`, `lstrip` i `rstrip` w Pythonie. Czym się różnią?

Python

Metody działają na brzegach stringa:

  • strip() — usuwa znaki z obu stron;
  • lstrip() — tylko z lewej;
  • rstrip() — tylko z prawej.

Domyślnie usuwają whitespace albo konkretny zestaw znaków:

"  hello  ".strip()      # "hello"
"--id--".strip("-")      # "id"

W skrócie:

  • Rodzina strip nie zmienia stringa in-place, tylko zwraca nowy.
  • Różnica dotyczy wyłącznie strony czyszczenia.
  • Często jest używana na danych wejściowych.
35. Opisz przeznaczenie metody `count` w stringach. Jak działa?

Python

str.count(sub[, start[, end]]) liczy liczbę niepokrywających się wystąpień podciągu sub w wybranym zakresie.

"banana".count("an")      # 2
"banana".count("a", 2)    # 2

Zwraca 0, jeśli nie ma żadnych wystąpień.

W skrócie:

  • count szybko zwraca liczbę wystąpień podciągu.
  • Obsługuje ograniczenie wyszukiwania przez start/end.
  • Liczy niepokrywające się wystąpienia.
36. Jak działa metoda `join` w Pythonie i do czego najczęściej się jej używa?

Python

sep.join(iterable) łączy sekwencję stringów w jeden string przy użyciu separatora sep.

names = ["Ada", "Linus", "Guido"]
result = ", ".join(names)  # "Ada, Linus, Guido"

Typowe zastosowania: tworzenie linii podobnych do CSV, logów, fragmentów SQL, ścieżek URL i komunikatów.

W skrócie:

  • join to standardowy i szybki sposób łączenia stringów.
  • Wywołuje się go na separatorze, a nie na liście.
  • Jest wydajniejszy niż wielokrotne += w pętli.
37. Czym jest slicing w Pythonie i jak za jego pomocą pobierać podciągi? Podaj przykłady z dodatnimi i ujemnymi indeksami.

Python

Slicing to wybór fragmentu sekwencji przez [start:stop:step].

s = "python"
s[1:4]     # "yth"
s[:2]      # "py"
s[-3:]     # "hon"
s[::-1]    # "nohtyp"

start jest włączony, stop nie jest włączony.

W skrócie:

  • Slicing działa dla stringów, list i krotek.
  • Ujemne indeksy są liczone od końca.
  • To podstawowe narzędzie obróbki sekwencji bez pętli.
38. Wyjaśnij metodę `replace` dla stringów. Jak za jej pomocą zamienić kilka wystąpień podciągu?

Python

str.replace(old, new, count=-1) zwraca nowy string, w którym old został zamieniony na new. Domyślnie zamieniane są wszystkie wystąpienia.

"foo bar foo".replace("foo", "baz")      # "baz bar baz"
"foo bar foo".replace("foo", "baz", 1)   # "baz bar foo"

W skrócie:

  • replace nie zmienia stringa in-place.
  • Bez count zamienia wszystkie wystąpienia.
  • count pozwala kontrolować liczbę zamian.
39. Jak działa metoda `split` w stringach?

Python

split(sep=None, maxsplit=-1) dzieli string na listę podciągów.

  • bez sep dzieli po dowolnych białych znakach;
  • z sep dzieli po konkretnym separatorze;
  • maxsplit ogranicza liczbę podziałów.
"a,b,c".split(",")         # ["a", "b", "c"]
"a b   c".split()          # ["a", "b", "c"]
"k=v=x".split("=", 1)      # ["k", "v=x"]

W skrócie:

  • split zamienia string na listę tokenów.
  • sep=None ma specjalne zachowanie dla whitespace.
  • maxsplit jest przydatny przy parsowaniu formatów key/value.
40. Jak działa konwersja typów (type casting)?

Python

Type casting to jawna konwersja wartości między typami przez konstruktory: int(), float(), str(), bool(), list(), tuple(), set(), dict().

age = int("42")
price = float("19.99")
text = str(10)

Niepoprawne dane powodują wyjątek (ValueError, TypeError), dlatego dla wejścia zewnętrznego potrzebna jest walidacja.

W skrócie:

  • Python udostępnia jawne funkcje konwersji typów.
  • Nie wszystkie wartości można bezpiecznie skonwertować.
  • Dla user input potrzebne są walidacja i obsługa wyjątków.
41. Jak Python automatycznie konwertuje różne typy danych na wartości boolean?

Python

W kontekście boolean Python stosuje reguły truthiness.

Wartości podobne do False:

  • False, None;
  • liczby zerowe: 0, 0.0, 0j;
  • puste kolekcje: "", [], {}, set(), tuple(), range(0).

Wszystko inne jest zwykle True.

bool([])      # False
bool("0")     # True

W skrócie:

  • Konwersja boolean opiera się na regułach truthy/falsy.
  • Puste i zerowe -> False.
  • Niepusty string "0" nadal jest True.
42. Wyjaśnij, jak zamienić string na liczbę całkowitą albo zmiennoprzecinkową w Pythonie. Co się stanie, jeśli stringa nie da się przekonwertować na liczbę?

Python

Do konwersji używaj int() i float():

count = int("42")
ratio = float("3.14")

Jeśli string jest niepoprawny, Python zgłasza ValueError.

def parse_int(value: str) -> int | None:
    try:
        return int(value)
    except ValueError:
        return None

W skrócie:

  • int() i float() wykonują jawną konwersję ze stringa.
  • Nieprawidłowy format -> ValueError.
  • Dla danych zewnętrznych potrzebny jest try/except.
43. Co robi Python przy porównywaniu różnych typów danych, takich jak liczby całkowite i zmiennoprzecinkowe albo liczby całkowite i boolean?

Python

Python pozwala na porównania liczbowe zgodnych typów numeric.

  • int i float są porównywane według wartości.
  • bool jest podklasą int: False == 0, True == 1.
1 == 1.0      # True
True == 1     # True
False < 1     # True

Dla niezgodnych typów, na przykład int i str, porównanie porządku (<, >) powoduje TypeError.

W skrócie:

  • int, float i bool mają wspólną semantykę numeric.
  • bool zachowuje się jak 0/1 w porównaniach.
  • Niezgodne typy nie są porównywane operatorami porządku.
44. Jak Python obsługuje porównania między listami a set?

Python

list i set to różne typy o różnej semantyce, dlatego bezpośrednie porównanie porządku między nimi nie jest wspierane.

[1, 2] == {1, 2}   # False
[1, 2] < {1, 2}    # TypeError

Jeśli trzeba porównać skład elementów, najpierw ujednolić typ:

set([1, 2]) == {1, 2}  # True

W skrócie:

  • list i set są porównywane jako różne struktury danych.
  • == między nimi zwykle daje False.
  • Aby porównać sensownie zawartość, najpierw sprowadź do jednego typu.
45. Opisz użycie funkcji `bool()` w Pythonie.

Python

bool(x) zwraca wartość boolean dla x zgodnie z regułami truthiness.

Typowe scenariusze:

  • jawna konwersja wartości do True/False;
  • czytelne warunki;
  • budowanie filtrów.
bool("text")   # True
bool("")       # False
bool(0)        # False

W skrócie:

  • bool() ujednolica sprawdzenie „puste/niepuste”.
  • Opiera się na wbudowanych regułach truthy/falsy.
  • Jest przydatne w walidacji i logice warunkowej.
46. Jaka jest różnica między `list` a `tuple`?

Python

Główna różnica: list jest mutable, a tuple immutable.

Praktyczne konsekwencje:

  • list nadaje się do danych, które się zmieniają;
  • tuple nadaje się do stałych rekordów;
  • tuple można używać jako klucza słownika, jeśli jego elementy są hashowalne.
coords: tuple[float, float] = (50.45, 30.52)
queue: list[str] = ["task-1", "task-2"]

W skrócie:

  • list służy do zmiennych kolekcji.
  • tuple służy do niezmiennych struktur danych.
  • Wybór wpływa na bezpieczeństwo API i użycie w dict/set.
47. Jak można utworzyć `tuple`?

Python

Podstawowe sposoby:

t1 = (1, 2, 3)
t2 = 1, 2, 3
t3 = tuple([1, 2, 3])
single = (42,)     # ważny przecinek
empty = ()

Dla jednego elementu przecinek jest obowiązkowy, inaczej to tylko wyrażenie w nawiasach.

W skrócie:

  • tuple tworzy się przez literał albo tuple(iterable).
  • single = (x,) to poprawny jednoelementowy tuple.
  • Pusty tuple: ().
48. Jaka jest różnica między metodą `reverse` a slicingiem `[::-1]` przy odwracaniu listy?

Python

list.reverse() zmienia istniejącą listę in-place i zwraca None.

lst[::-1] tworzy nową odwróconą listę, czyli kopię.

values = [1, 2, 3]
values.reverse()      # values -> [3, 2, 1]

other = values[::-1]  # nowa lista

W skrócie:

  • reverse() zmienia oryginał.
  • [::-1] zwraca nowy obiekt.
  • Wybór zależy od potrzeby zachowania danych wejściowych.
49. Wyjaśnij przeznaczenie i użycie metody `sort` dla list. Jak posortować listę w porządku malejącym?

Python

list.sort() sortuje listę in-place. Obsługuje parametry:

  • key — funkcja klucza sortowania;
  • reverse=True — porządek malejący.
nums = [5, 1, 7]
nums.sort(reverse=True)  # [7, 5, 1]

Jeśli potrzebna jest nowa, posortowana kopia, użyj sorted(iterable).

W skrócie:

  • sort() zmienia listę na miejscu.
  • Dla porządku malejącego użyj reverse=True.
  • sorted() jest wygodne, gdy trzeba zachować oryginał.
50. Jaka jest różnica między metodą `copy` a bezpośrednim przypisaniem jednej listy do drugiej? Dlaczego czasem ważne jest użycie `copy`?

Python

Przypisanie kopiuje tylko referencję:

a = [1, 2]
b = a
b.append(3)   # a też się zmieni

a.copy() tworzy nową, płytką listę:

a = [1, 2]
b = a.copy()
b.append(3)   # a się nie zmieni

Dla struktur zagnieżdżonych może być potrzebne copy.deepcopy.

W skrócie:

  • = nie kopiuje danych, tylko współdzieli jeden obiekt między zmiennymi.
  • copy() izoluje zmiany na poziomie listy zewnętrznej.
  • Dla obiektów zagnieżdżonych używaj deepcopy.
51. Co robi metoda `pop` w liście? Czym różni się jej zachowanie, jeśli podać indeks, a jeśli nie?

Python

pop() usuwa i zwraca element z listy.

  • pop() bez argumentów usuwa ostatni element;
  • pop(i) usuwa element o indeksie i.
items = ["a", "b", "c"]
last = items.pop()     # "c"
first = items.pop(0)   # "a"

Błędny indeks powoduje IndexError.

W skrócie:

  • pop łączy usunięcie elementu z jego zwróceniem.
  • Bez indeksu działa na końcu listy.
  • Z indeksem usuwa konkretną pozycję.
52. Jak połączyć dwie listy w Pythonie za pomocą operatora `+` i metody `extend`? Jaka jest kluczowa różnica między tymi dwoma sposobami?

Python

a + b tworzy nową listę, a a.extend(b) modyfikuje listę a in-place.

a = [1, 2]
b = [3, 4]

c = a + b         # [1, 2, 3, 4], a się nie zmieniło
a.extend(b)       # a -> [1, 2, 3, 4]

W skrócie:

  • + zwraca nowy obiekt.
  • extend() mutuje istniejącą listę.
  • Wybór zależy od tego, czy trzeba zachować oryginał.
53. Do czego służą funkcje `min`, `max`, `sum`, `all`, `any` w kontekście list?

Python

To podstawowe agregatory dla iterowalnych kolekcji:

  • min() / max() — minimum i maksimum;
  • sum() — suma liczb;
  • all()True, jeśli wszystkie elementy są truthy;
  • any()True, jeśli przynajmniej jeden element jest truthy.
nums = [3, 10, 1]
min(nums), max(nums), sum(nums)  # (1, 10, 14)
all([True, 1, "x"])              # True
any([0, "", None, 5])            # True

W skrócie:

  • Funkcje zmniejszają ilość boilerplate w pętlach.
  • Działają z dowolnym iterable.
  • Dobrze łączą się z generatorami do leniwego przetwarzania.
54. Czym jest słownik (`dict`) w Pythonie i czym różni się od innych struktur danych?

Python

dict to tablica asocjacyjna: przechowuje pary klucz -> wartość. Klucze muszą być hashable, a wartości mogą mieć dowolny typ.

Różnice:

  • dostęp odbywa się po kluczu, a nie po indeksie;
  • średnia złożoność wyszukiwania/wstawiania/usuwania jest bliska O(1);
  • od Pythona 3.7+ zachowuje kolejność wstawiania kluczy.

W skrócie:

  • dict jest optymalny do szybkiego dostępu po kluczu.
  • To podstawowa struktura dla konfiguracji i mapowań.
  • Klucze muszą być niemutowalne (hashable).
55. Jaka jest różnica między pobieraniem wartości ze słownika przez nawiasy kwadratowe `[]` a metodą `get`?

Python

d[key] zwraca wartość albo zgłasza KeyError, jeśli klucza nie ma.

d.get(key, default) zwraca wartość albo default (lub None) bez wyjątku.

config = {"timeout": 30}
config["timeout"]         # 30
config.get("retries", 3)  # 3

W skrócie:

  • [] służy do obowiązkowych kluczy.
  • get() nadaje się do opcjonalnych pól.
  • get() zmniejsza liczbę try/except przy odczycie danych.
56. Opisz, jak można aktualizować i usuwać elementy ze słownika.

Python

Aktualizacja:

  • d[key] = value — wstawić/zaktualizować jeden klucz;
  • d.update({...}) — aktualizacja zbiorcza.

Usuwanie:

  • del d[key] — usunąć klucz (błąd, jeśli go nie ma);
  • d.pop(key, default) — usunąć i zwrócić wartość;
  • d.popitem() — usunąć ostatnią parę;
  • d.clear() — wyczyścić cały słownik.

W skrócie:

  • Do upsertu nadają się [] i update().
  • Do bezpiecznego usuwania częściej używa się pop().
  • API warto dobierać zależnie od oczekiwanego zachowania przy braku klucza.
57. Do czego służą metody `keys`, `values` oraz `items` w słownikach?

Python

Te metody zwracają obiekty view słownika:

  • keys() — wszystkie klucze;
  • values() — wszystkie wartości;
  • items() — pary (key, value).
data = {"a": 1, "b": 2}
for key, value in data.items():
    ...

Widoki są dynamiczne: odzwierciedlają aktualny stan słownika.

W skrócie:

  • keys/values/items są potrzebne do iteracji i sprawdzeń.
  • items() jest najwygodniejsze do pętli z kluczem i wartością.
  • To nie są kopie, tylko "żywe" reprezentacje danych.
58. Jak `dict` działa w Pythonie pod maską?

Python

dict jest zaimplementowany jako hash table z optymalizacjami pamięci i szybkiego dostępu. Klucz jest haszowany, na podstawie hasza wybierana jest komórka, a kolizje są rozwiązywane wewnętrznym algorytmem probing.

Praktyczne konsekwencje:

  • średni czas dostępu jest bliski O(1);
  • jakość __hash__ i __eq__ wpływa na zachowanie;
  • obiektów mutable nie można używać jako kluczy.

W skrócie:

  • dict to wysokowydajna struktura hashująca.
  • Szybkość osiąga dzięki haszowaniu.
  • Klucze muszą być hashable i stabilne.
59. Czym jest hash function?

Python

Hash function przekształca obiekt w liczbę całkowitą (hash), która jest używana do szybkiego umieszczania/wyszukiwania w dict i set.

Warunki poprawności:

  • jeśli a == b, to hash(a) == hash(b);
  • wartość hasza musi być stabilna przez cały czas życia obiektu.

W skrócie:

  • Funkcja haszująca jest podstawą szybkiego działania dict/set.
  • Nie gwarantuje unikalności, bo kolizje są możliwe.
  • Dla klas custom ważna jest spójność __eq__ i __hash__.
60. Czym jest `set` w Pythonie i czym różni się od innych struktur danych?

Python

set to nieuporządkowana kolekcja unikalnych elementów.

Różnice:

  • automatycznie usuwa duplikaty;
  • oferuje szybkie operacje sprawdzania przynależności (x in s);
  • wspiera operacje zbiorowe: union/intersection/difference.

W skrócie:

  • set jest optymalny do zapewniania unikalności i membership-check.
  • Kolejność elementów nie jest gwarantowana.
  • Elementy muszą być hashable.
61. Jak utworzyć `set` w Pythonie i jakie ograniczenia dotyczą elementów set?

Python

Tworzenie:

s1 = {1, 2, 3}
s2 = set([1, 2, 2, 3])   # {1, 2, 3}
empty = set()            # nie {}

Ograniczenia:

  • elementy muszą być hashable, na przykład int, str, tuple;
  • list, dict, set nie można dodać bezpośrednio.

W skrócie:

  • set() tworzy pusty zbiór.
  • Duplikaty są usuwane automatycznie.
  • Dozwolone są tylko elementy hashable.
62. Do czego służy metoda `intersection()` w Python set i czym różni się od `union()`?

Python

intersection() zwraca wspólne elementy zbiorów, a union() łączy wszystkie unikalne elementy z obu zbiorów.

a = {1, 2, 3}
b = {3, 4}

a.intersection(b)  # {3}
a.union(b)         # {1, 2, 3, 4}

W skrócie:

  • intersection = część wspólna.
  • union = suma zbiorów, czyli wszystko unikalne.
  • Obie metody są podstawowe przy porównywaniu zestawów danych.
63. Jakie typy danych są immutable?

Python

Popularne typy immutable:

  • int, float, bool, complex;
  • str, bytes;
  • tuple jeśli jego elementy też są immutable;
  • frozenset;
  • NoneType.

W skrócie:

  • Obiektu immutable nie można zmienić po utworzeniu.
  • Zamiast mutacji powstaje nowy obiekt.
  • Takie typy są bezpieczniejsze jako klucze dict i elementy set.
64. Jakie typy danych są mutable?

Python

Popularne typy mutable:

  • list;
  • dict;
  • set;
  • bytearray;
  • większość obiektów klas zdefiniowanych przez użytkownika.

W skrócie:

  • Obiekty mutable zmieniają się in-place.
  • Mutacje mogą powodować efekty uboczne przez współdzielone referencje.
  • Z kopiowaniem trzeba pracować ostrożnie.
65. Dlaczego ważne jest rozumienie mutowalności podczas pracy ze słownikami albo set?

Python

dict i set opierają się na haszowaniu, dlatego ich elementy i klucze muszą być stabilne (hashable). Obiektów mutable nie da się bezpiecznie używać jako kluczy ani elementów set.

Dodatkowo mutacja wartości przez współdzielone referencje często prowadzi do trudnych do przewidzenia błędów.

W skrócie:

  • Mutowalność wpływa na poprawność kluczy w dict/set.
  • Współdzielone obiekty mutable często powodują efekty uboczne.
  • Jawne kopie i kontrola mutacji zmniejszają liczbę błędów.
66. Czym jest `None`?

Python

None to specjalna wartość singleton typu NoneType, która oznacza "brak wartości".

Typowe scenariusze:

  • funkcja niczego jawnie nie zwraca;
  • opcjonalne parametry;
  • znacznik "dane nie są jeszcze ustawione".

Sprawdzenie wykonuje się przez is:

if value is None:
    ...

W skrócie:

  • None oznacza brak wartości.
  • To singleton, więc porównuje się go przez is.
  • Często jest używany w API jako stan opcjonalny.
67. Czym jest `id` w Pythonie, jak go używać i dlaczego to ważne?

Python

id(obj) zwraca identyfikator obiektu, unikalny w ramach cyklu życia obiektu w bieżącym procesie.

To przydaje się w diagnostyce:

  • czy to ten sam obiekt;
  • czy została utworzona kopia;
  • czy nastąpiła mutacja współdzielonej referencji.

W skrócie:

  • id pomaga analizować tożsamość obiektów.
  • Jest przydatny przy debugowaniu kopiowania i mutacji.
  • Nie używa się go jako identyfikatora biznesowego.
68. Jaka jest różnica między operatorami `is` i `==`?

Python

== porównuje wartości czyli równoważność, a is porównuje tożsamość, czyli czy to dokładnie ten sam obiekt w pamięci.

a = [1, 2]
b = [1, 2]
a == b   # True
a is b   # False

Ważne o optymalizacjach (interning): CPython buforuje małe liczby całkowite od -5 do 256 oraz krótkie napisy na etapie kompilacji lub ładowania. Dlatego dla nich is może zwracać True, nawet jeśli zostały utworzone osobno. To jednak szczegół implementacyjny, na którym nie należy opierać logiki biznesowej.

Dla None zawsze używaj is.

W skrócie:

  • == dotyczy równości wartości.
  • is dotyczy dokładnie tego samego obiektu w pamięci.
  • Interning może dawać nieoczywiste is True dla małych int i str.
  • value is None to jedyny poprawny styl sprawdzania None.
69. Jak stosuje się Singleton pattern w Pythonie? Podaj przykłady obiektów typu Singleton w Pythonie.

Python

Singleton oznacza jedną globalną instancję obiektu w procesie. W Pythonie często zastępuje się go poziomem modułu, ponieważ moduły są importowane tylko raz.

Przykłady singletonów w języku:

  • None;
  • True i False;
  • Ellipsis.

W kodzie aplikacyjnym zamiast sztywnego Singletonu częściej używa się kontenera DI albo fabryk, żeby poprawić testowalność.

W skrócie:

  • Python ma już wbudowane obiekty typu singleton.
  • Często wystarcza zakres modułu bez osobnego wzorca.
  • Nadużywanie Singletonu pogarsza testowalność.
70. Do czego służą operatory `break` i `continue` w pętlach Pythona?

Python

break przedwcześnie kończy pętlę, a continue pomija bieżącą iterację i przechodzi do następnej.

for n in range(10):
    if n == 5:
        break
    if n % 2 == 0:
        continue

W skrócie:

  • break zatrzymuje całą pętlę.
  • continue pomija tylko bieżący krok.
  • Dzięki nim sterowanie pętlą jest jawne i czytelne.
71. Wyjaśnij pojęcie pętli nieskończonej. W jakich przypadkach warto jej używać i jak zapewnić jej poprawne zakończenie?

Python

Pętla nieskończona to pętla bez naturalnego warunku zakończenia, na przykład while True. Jest używana w procesach usługowych, workerach, pollingu i logice pętli zdarzeń.

Poprawne zakończenie:

  • wyraźny warunek break;
  • obsługa sygnałów zakończenia;
  • timeouty i try/finally do czyszczenia zasobów.
while True:
    task = queue.get()
    if task is None:
        break
    handle(task)

W skrócie:

  • while True jest uzasadnione w długowiecznych pętlach usługowych.
  • Potrzebny jest kontrolowany mechanizm zatrzymania.
  • Zawsze trzeba przewidzieć zwalnianie zasobów.
72. Opisz, jak działają zagnieżdżone pętle w Pythonie. Jakie problemy z wydajnością mogą się pojawić i jak ich unikać?

Python

Pętla zagnieżdżona to pętla wewnątrz innej pętli. Złożoność często rośnie do O(n*m) albo gorzej, co jest krytyczne przy dużych zbiorach danych.

Optymalizacje:

  • zamieniać wyszukiwanie w listach na set lub dict;
  • wynosić niezmienniki poza pętlę wewnętrzną;
  • używać generatorów, itertools i wektoryzacji;
  • profilować "gorące" miejsca.

W skrócie:

  • Zagnieżdżone pętle szybko zwiększają koszt obliczeń.
  • Struktury danych są często ważniejsze niż mikrooptymalizacje.
  • Profilowanie pokazuje, co naprawdę trzeba optymalizować.
73. Czym jest structural pattern matching (`match`/`case`)?

Python

match/case w Pythonie 3.10+ to mechanizm dopasowywania struktury danych do wzorców. Działa z literałami, typami, sekwencjami, słownikami i klasami.

def handle(message: dict[str, object]) -> str:
    match message:
        case {"type": "ping"}:
            return "pong"
        case {"type": "user", "id": int(user_id)}:
            return f"user:{user_id}"
        case _:
            return "unknown"

W skrócie:

  • match/case jest czytelniejsze przy złożonym rozgałęzieniu.
  • Pozwala jednocześnie sprawdzać kształt danych i wyciągać wartości.
  • Jest szczególnie przydatne przy protokołach, zdarzeniach i parsowaniu struktur.
74. W jakich przypadkach pattern matching jest lepszy niż `if`/`elif`?

Python

match/case jest lepszy, gdy trzeba:

  • sprawdzać wiele wzajemnie wykluczających się form danych;
  • rozpakowywać zagnieżdżone struktury;
  • unikać długich łańcuchów if/elif.

if/elif jest lepsze dla prostych warunków logicznych i krótkiej logiki.

W skrócie:

  • Dla scenariuszy strukturalnych lepszy jest match/case.
  • Dla prostych warunków wystarczy if/elif.
  • Kryterium wyboru to czytelność i łatwość utrzymania.
75. Czym jest funkcja w Pythonie?

Python

Funkcja to nazwany blok kodu typu callable, który przyjmuje argumenty, zwraca wynik i pozwala enkapsulować logikę.

def normalize_name(name: str) -> str:
    return name.strip().title()

Funkcje wspierają wartości domyślne, argumenty nazwane, *args/**kwargs, adnotacje typów i dekoratory.

W skrócie:

  • Funkcja jest podstawową jednostką ponownego użycia kodu.
  • Tworzy wyraźny kontrakt przez parametry i wartość return.
  • Type hints czynią ten kontrakt bardziej jawnym.
76. Jakie istnieją typy argumentów funkcji?

Python

We współczesnym Pythonie:

  • positional-only (/);
  • positional-or-keyword;
  • keyword-only (*);
  • variadic positional (*args);
  • variadic keyword (**kwargs).
def f(a, /, b, *, c, **kwargs):
    ...

W skrócie:

  • Python daje elastyczną kontrolę nad sposobem wywołania funkcji.
  • / i * formalizują kontrakt API.
  • *args/**kwargs są przydatne w rozszerzalnych interfejsach.
77. Czym są argumenty pozycyjne i nazwane?

Python

Argumenty pozycyjne są przekazywane według kolejności, a nazwane (keyword) według nazwy parametru.

def connect(host: str, port: int) -> str:
    return f"{host}:{port}"

connect("localhost", 5432)           # pozycyjnie
connect(host="localhost", port=5432) # nazwane

W skrócie:

  • Pozycyjne zależą od kolejności parametrów.
  • Nazwane zwiększają czytelność wywołania.
  • Można je łączyć z zachowaniem zasad sygnatury.
78. Czym są argumenty domyślne i jakie problemy mogą z nimi występować?

Python

Argumenty domyślne są używane, jeśli wartość nie została przekazana przy wywołaniu. Są obliczane tylko raz w momencie definiowania funkcji.

Problem: mutable default.

def add_item(item: int, bucket: list[int] | None = None) -> list[int]:
    if bucket is None:
        bucket = []
    bucket.append(item)
    return bucket

W skrócie:

  • Wartości domyślne są wygodne dla stabilnych danych.
  • Mutable default może kumulować stan między wywołaniami.
  • Bezpieczny wzorzec to None + inicjalizacja wewnątrz funkcji.
79. Wyjaśnij przeznaczenie i użycie `*args` oraz `**kwargs` w funkcjach Pythona. Czym się różnią?

Python

*args zbiera dodatkowe argumenty pozycyjne do tuple. **kwargs zbiera dodatkowe argumenty nazwane do dict.

def log_event(event: str, *args: object, **kwargs: object) -> None:
    ...

Zastosowania: wrappery, adaptery API, dekoratory i przekazywanie parametrów dalej.

W skrócie:

  • *args = dodatkowe argumenty pozycyjne.
  • **kwargs = dodatkowe argumenty nazwane.
  • Dają funkcjom elastyczność, ale wymagają jasnej walidacji.
80. Jak zdefiniować funkcję z type annotations w Pythonie? Podaj przykład i wyjaśnij zalety tego podejścia.

Python

Typy zapisuje się w sygnaturze parametrów i wartości return.

def process_users(users: list[dict[str, object]]) -> list[str]:
    return [str(user["name"]) for user in users if bool(user.get("active"))]

Zalety:

  • lepszy DX, czyli autocomplete i nawigacja;
  • statyczne sprawdzanie w CI;
  • jawny kontrakt dla innych programistów.

W skrócie:

  • Adnotacje typów dokumentują API.
  • Zmniejszają ryzyko błędów integracyjnych.
  • Najwięcej dają w średnich i dużych bazach kodu.
81. Czym są funkcje lambda?

Python

lambda to anonimowa funkcja jedno-wyrażeniowa. Zwykle jest używana do krótkich callbacków w sorted, map, filter.

users = [{"name": "Ada"}, {"name": "Bob"}]
users_sorted = sorted(users, key=lambda u: u["name"])

Dla bardziej złożonej logiki lepsza jest zwykła funkcja def.

W skrócie:

  • lambda jest wygodna do krótkich lokalnych wyrażeń.
  • Jest ograniczona do jednego wyrażenia.
  • Dla czytelności bardziej złożony kod lepiej wynieść do def.
82. Jaki jest zakres widoczności zmiennych w funkcji?

Python

Python używa reguły LEGB do wyszukiwania nazw:

  • Local;
  • Enclosing, czyli funkcje zewnętrzne;
  • Global, czyli moduł;
  • Builtins.

Wewnątrz funkcji przypisanie tworzy zmienną lokalną, jeśli nie użyto global albo nonlocal.

W skrócie:

  • Scope określa, gdzie nazwa jest dostępna i może być zmieniana.
  • LEGB wyjaśnia kolejność wyszukiwania zmiennych.
  • Błędne rozumienie zasięgu często prowadzi do UnboundLocalError.
83. Czym są zmienne lokalne i globalne w Pythonie?

Python

Zmienne lokalne istnieją w ciele funkcji. Zmienne globalne są zdefiniowane na poziomie modułu.

Aby zmienić zmienną globalną z poziomu funkcji, potrzebny jest global, ale zwykle warto tego unikać przez niejawne zależności.

W skrócie:

  • Zmienne lokalne są bezpieczniejsze dla utrzymania i testowania.
  • Globalne upraszczają dostęp, ale utrudniają kontrolę stanu.
  • Lepiej przekazywać zależności przez parametry.
84. Jaka jest różnica między zmiennymi lokalnymi a `nonlocal` w Pythonie?

Python

nonlocal jest używane w funkcji zagnieżdżonej do zmiany zmiennej z najbliższego zewnętrznego zasięgu, czyli nie globalnego.

from collections.abc import Callable


def counter() -> Callable:
    value = 0

    def inc() -> int:
        nonlocal value
        value += 1
        return value

    return inc

W skrócie:

  • Zmienna lokalna należy do bieżącej funkcji.
  • nonlocal zmienia stan funkcji zewnętrznej.
  • To kluczowy mechanizm dla closure ze stanem.
85. Czym jest unpacking w Pythonie i jak stosuje się go przy przypisaniu?

Python

Unpacking to rozkładanie elementów sekwencji lub struktury na osobne zmienne.

x, y = (10, 20)
first, *middle, last = [1, 2, 3, 4]

Działa też ze słownikami w match/case, przy wywołaniach funkcji i w pętlach.

W skrócie:

  • Unpacking sprawia, że kod jest bardziej zwięzły i czytelny.
  • Wspiera "gwiazdkowe" przechwycenie pozostałych wartości.
  • Często jest używany przy parsowaniu struktur danych.
86. Wyjaśnij pojęcie packing wartości w Pythonie i podaj przykłady.

Python

Packing to zbieranie kilku wartości do jednej struktury, na przykład tuple, list, dict.

point = 10, 20                 # tuple packing

def collect(*args: int) -> tuple[int, ...]:
    return args

*args i **kwargs to typowy przykład packingu argumentów.

W skrócie:

  • Packing zbiera wiele wartości do jednego kontenera.
  • Najczęściej jest używany w sygnaturach funkcji.
  • Dobrze łączy się z unpackingiem po stronie wywołania.
87. Do czego używa się operatora `*` przy packingu i unpackingu?

Python

* w parametrach funkcji pakuje argumenty pozycyjne do *args, a przy wywołaniu rozpakowuje iterable do argumentów pozycyjnych.

def add(a: int, b: int) -> int:
    return a + b

nums = [2, 3]
add(*nums)  # 5

* jest też używane w przypisaniu do przechwycenia "reszty" elementów.

W skrócie:

  • * to uniwersalny operator do pracy z varargs.
  • W sygnaturze pakuje, przy wywołaniu rozpakowuje.
  • Zmniejsza ilość szablonowego kodu przy przekazywaniu danych.
88. Czym jest closure i jaki ma związek z decoratorami?

Python

Closure to funkcja wewnętrzna, która "pamięta" zmienne z zewnętrznego zakresu nawet po zakończeniu działania funkcji zewnętrznej.

Decorator zwykle implementuje się właśnie przez closure: wrapper przechowuje odwołanie do oryginalnej funkcji i dodatkowych parametrów.

W skrócie:

  • Closure to funkcja plus przechwycony kontekst.
  • Decorator jest często praktycznym zastosowaniem closure.
  • Daje możliwość dodania zachowania bez zmiany ciała funkcji.
89. Czym jest decorator w Pythonie i jak działa?

Python

Decorator to obiekt callable, który przyjmuje funkcję albo klasę i zwraca zmodyfikowaną wersję, czyli wrapper.

from functools import wraps

def log_calls(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

Zastosowania: logowanie, cache'owanie, autoryzacja, retry i metryki.

W skrócie:

  • Decorator dodaje zachowanie cross-cutting.
  • Działa przez opakowanie callable.
  • Pozwala nie duplikować technicznej logiki w kodzie biznesowym.
90. Czy można używać kilku decoratorów dla jednej funkcji?

Python

Tak, można układać kilka decoratorów w stos. Są stosowane od dołu do góry, czyli ten bliżej def opakowuje funkcję jako pierwszy.

@decorator_a
@decorator_b
def handler() -> None:
    ...

Równoważny zapis: handler = decorator_a(decorator_b(handler)).

W skrócie:

  • Kilka decoratorów jest dopuszczalne i powszechne.
  • Kolejność zastosowania ma znaczenie.
  • Stos decoratorów warto dokumentować dla przejrzystości.
91. Opisz możliwy problem z kolejnością decoratorów przy ich stosowaniu do funkcji.

Python

Nieprawidłowa kolejność może zmienić semantykę. Na przykład cache'owanie przed sprawdzeniem dostępu może zachować niepożądany wynik albo ominąć oczekiwaną logikę.

Typowe ryzyka:

  • logowanie widzi już zmienione argumenty;
  • retry opakowują nie ten wyjątek;
  • cache jest nakładany przed lub po walidacji w złym miejscu.

W skrócie:

  • Kolejność decoratorów wpływa na zachowanie funkcji.
  • To częsta przyczyna ukrytych błędów.
  • Dla krytycznych łańcuchów potrzebne są testy kolejności wykonania.
92. Czy można utworzyć decorator za pomocą klasy?

Python

Tak. Klasa-decorator implementuje __call__, aby instancja zachowywała się jak funkcja.

class CallCounter:
    def __init__(self, func):
        self.func = func
        self.calls = 0

    def __call__(self, *args, **kwargs):
        self.calls += 1
        return self.func(*args, **kwargs)

W skrócie:

  • Decorator można zaimplementować nie tylko jako funkcję, ale też jako klasę.
  • Klasa jest wygodna, gdy potrzebny jest wewnętrzny stan.
  • Kluczowy mechanizm to __call__.
93. Jak zdefiniować decorator przyjmujący parametry?

Python

Potrzebna jest trójpoziomowa struktura: fabryka dekoratora -> decorator -> wrapper.

from functools import wraps

def retry(times: int):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(times - 1):
                try:
                    return func(*args, **kwargs)
                except Exception:
                    pass
            return func(*args, **kwargs)
        return wrapper
    return decorator

W skrócie:

  • Parametryzowany decorator to funkcja, która zwraca decorator.
  • Do zachowania parametrów często używa się closure.
  • To podejście jest wygodne przy konfigurowalnym zachowaniu.
94. Do czego używa się `functools.wraps` w funkcjach-decoratorach?

Python

functools.wraps kopiuje metadane oryginalnej funkcji do wrappera: nazwę, docstring, moduł oraz __wrapped__.

To ważne dla:

  • poprawnego debugowania i logów;
  • introspekcji i dokumentacji;
  • zgodności z narzędziami, które odczytują sygnaturę.

W skrócie:

  • wraps zachowuje "tożsamość" oryginalnej funkcji.
  • Bez niego dekorowane funkcje tracą przydatne metadane.
  • To best practice dla wszystkich decoratorów opartych na wrapperze.
95. Jak działają dict comprehension, list comprehension i set comprehension?

Python

Comprehension tworzy kolekcję na podstawie wyrażenia i pętli, opcjonalnie z filtrem.

squares = [x * x for x in range(6)]
mapping = {x: x * x for x in range(6)}
unique = {x % 3 for x in range(10)}

Format:

  • list: [expr for x in it if cond]
  • set: {expr for x in it if cond}
  • dict: {k_expr: v_expr for x in it if cond}

W skrócie:

  • Comprehension w zwięzły sposób wyraża transformację danych.
  • Działa dla list, set i dict.
  • Daje czystszy kod niż ręczne dodawanie elementów w pętli.
96. Jakie są zalety używania list comprehension w porównaniu ze zwykłymi pętlami?

Python

Zalety:

  • krótszy i bardziej deklaratywny kod;
  • mniej zmiennych pomocniczych;
  • zwykle trochę lepsza wydajność w CPythonie;
  • mniejsze ryzyko, że zapomnisz o append.

Wada: przy bardzo złożonej logice comprehension pogarsza czytelność.

W skrócie:

  • List comprehension dobrze nadaje się do prostych transformacji.
  • Często jest szybsze i czystsze niż ręczna pętla.
  • Dla złożonych gałęzi lepszy jest zwykły for.
97. Czy możesz podać przykład zagnieżdżonej list comprehension?

Python

Przykład spłaszczenia macierzy:

matrix = [[1, 2], [3, 4], [5, 6]]
flat = [item for row in matrix for item in row]  # [1, 2, 3, 4, 5, 6]

Przykład z warunkiem:

pairs = [(x, y) for x in range(3) for y in range(3) if x != y]

W skrócie:

  • Zagnieżdżona comprehension to kilka for w jednym wyrażeniu.
  • Kolejność for odpowiada zagnieżdżonym pętlom.
  • Używaj jej wtedy, gdy wyrażenie pozostaje czytelne.
98. Co jest szybsze w Pythonie: list comprehension czy tworzenie listy za pomocą pętli?

Python

W typowych scenariuszach list comprehension jest trochę szybsze niż pętla z append, bo ma zoptymalizowany bajtkod i mniej narzutu.

Ważne: realny zysk zależy od samej operacji, dlatego w krytycznych miejscach warto mierzyć wydajność za pomocą timeit lub pyperf.

W skrócie:

  • Często szybsze jest list comprehension.
  • Różnica może być niewielka.
  • W rozwiązaniach produkcyjnych trzeba opierać się na pomiarach.
99. Czym jest iterator w Pythonie?

Python

Iterator to obiekt, który zwraca elementy sekwencyjnie i pamięta bieżący stan. Implementuje protokół:

  • __iter__() zwraca siebie;
  • __next__() zwraca następny element albo zgłasza StopIteration.

W skrócie:

  • Iterator daje dostęp element po elemencie bez pełnego wczytywania danych.
  • To podstawa dla for, generatorów i leniwego przetwarzania.
  • Po wyczerpaniu iterator nie "przewija się" sam.
100. Jak utworzyć iterator z obiektu iterowalnego za pomocą funkcji `iter()`?

Python

Wywołaj iter(iterable), aby otrzymać iterator.

items = [10, 20, 30]
it = iter(items)

Potem wartości odczytuje się przez next(it).

W skrócie:

  • iter() zamienia iterable na iterator.
  • To jawny sposób ręcznego sterowania iteracją.
  • Używa się go w niskopoziomowym przetwarzaniu strumienia danych.
101. Do czego służy funkcja `next()` przy pracy z iteratorami?

Python

next(iterator[, default]) zwraca następny element iteratora. Gdy elementy się skończą, zgłasza StopIteration albo zwraca default, jeśli został podany.

it = iter([1, 2])
next(it)        # 1
next(it)        # 2
next(it, None)  # None

W skrócie:

  • next() daje ręczną kontrolę nad krokiem iteracji.
  • Bez default koniec strumienia oznacza StopIteration.
  • Z default można wygodnie i bezpiecznie czytać strumień.
102. Czy można zamiennie używać metod `__next__()` i `__iter__()` z funkcjami `next()` oraz `iter()`?

Python

Tak, ale z uwzględnieniem protokołu:

  • iter(obj) wywołuje obj.__iter__();
  • next(it) wywołuje it.__next__().

Funkcje są więc standardowym interfejsem do metod dunder i zwykle używa się ich zamiast bezpośrednich wywołań.

W skrócie:

  • iter() odpowiada __iter__().
  • next() odpowiada __next__().
  • W kodzie aplikacyjnym lepiej wywoływać funkcje wbudowane.
103. Jaka jest rola `StopIteration` w projektowaniu iteratorów i kiedy zwykle się pojawia?

Python

StopIteration sygnalizuje, że iterator został wyczerpany. Pętla for automatycznie go przechwytuje i kończy iterację.

Zwykle pojawia się:

  • przy wywołaniu next(it) po ostatnim elemencie;
  • we własnych iteratorach, gdy dane się skończyły.

W skrócie:

  • StopIteration to normalny koniec strumienia.
  • Nie trzeba go logować jako "błędu" w normalnym przepływie.
  • We własnych iteratorach trzeba zgłaszać go poprawnie.
104. Czym jest generator i czym różni się od iteratora albo zwykłej funkcji?

Python

Generator to specjalny iterator tworzony przez funkcję z yield. Zwraca wartości po jednej i zachowuje wewnętrzny stan między wywołaniami.

Różnice:

  • od zwykłej funkcji: nie kończy się jednym return, tylko działa w trybie "pauza/wznowienie";
  • od ręcznego iteratora: ma prostszą implementację bez jawnej klasy z __next__.

W skrócie:

  • Generator to najwygodniejszy sposób leniwej iteracji.
  • Wymaga mniej kodu niż własna klasa iteratora.
  • Jest szczególnie przydatny dla dużych strumieni danych.
105. Jak utworzyć funkcję generatora?

Python

Trzeba zdefiniować funkcję z yield.

def countdown(start: int):
    current = start
    while current > 0:
        yield current
        current -= 1

Wywołanie countdown(3) zwraca obiekt generatora, po którym można iterować.

W skrócie:

  • Obecność yield sprawia, że funkcja staje się generatorem.
  • Generator zwraca wartości etapami.
  • Stan funkcji jest zachowywany między iteracjami.
106. Jak słowo kluczowe `yield` zapewnia działanie generatorów i dlaczego oszczędzają one pamięć?

Python

yield zwraca kolejną wartość i "zamraża" kontekst funkcji, czyli zmienne lokalne oraz miejsce wykonania. Następne next() wznawia działanie od tego punktu.

Oszczędność pamięci wynika z tego, że dane nie są tworzone w całości z góry, tylko obliczane na żądanie.

W skrócie:

  • yield realizuje mechanizm pauzy i wznowienia wykonania.
  • Generator wspiera leniwe obliczanie.
  • To zmniejsza zużycie pamięci dla dużych zbiorów danych.
107. Jaka jest różnica między `return` a `yield`?

Python

return kończy funkcję i zwraca jedną końcową wartość. yield zwraca wartość pośrednią i zachowuje stan do dalszego wznowienia.

W generatorze return oznacza koniec iteracji, czyli StopIteration.

W skrócie:

  • return oznacza zakończenie funkcji.
  • yield oznacza etapowe zwracanie wartości.
  • yield jest używane przy przetwarzaniu strumieniowym.
108. Czym jest Object-Oriented Programming (OOP) i jakie są jego główne zasady w Pythonie?

Python

OOP to podejście, w którym dane i zachowanie są połączone w obiektach.

Główne zasady:

  • enkapsulacja;
  • dziedziczenie;
  • polimorfizm;
  • abstrakcja.

W Pythonie OOP zwykle łączy się z kompozycją i duck typing.

W skrócie:

  • OOP strukturyzuje domenę przez klasy i obiekty.
  • Python wspiera OOP elastycznie, bez nadmiaru szablonowego kodu.
  • W praktyce kompozycja często bywa lepsza niż głębokie dziedziczenie.
109. Czym jest `class` w Pythonie?

Python

class to szablon do tworzenia obiektów z atrybutami i metodami.

class User:
    def __init__(self, name: str) -> None:
        self.name = name

Klasa określa strukturę i zachowanie przyszłych instancji.

W skrócie:

  • Klasa opisuje dane i operacje na nich.
  • Na podstawie klasy tworzone są obiekty, czyli instancje.
  • To podstawowa jednostka modelowania w OOP.
110. Jak utworzyć obiekt w Pythonie?

Python

Obiekt tworzy się przez wywołanie klasy:

class User:
    def __init__(self, name: str) -> None:
        self.name = name

user = User("Ada")

Podczas tworzenia wykonywane jest __init__, aby zainicjalizować stan.

W skrócie:

  • Obiekt to instancja klasy.
  • Tworzenie wygląda tak: instance = ClassName(...).
  • __init__ ustawia początkowe atrybuty.
111. Czym są obiekty i atrybuty?

Python

Obiekt to konkretna instancja typu albo klasy. Atrybut to powiązana z obiektem para nazwa-wartość, czyli dane albo metoda.

user.name       # atrybut-dane
user.save()     # atrybut-metoda

W skrócie:

  • Obiekt przechowuje stan i zachowanie.
  • Atrybuty opisują ten stan i zachowanie.
  • Dostęp do atrybutów odbywa się przez notację kropkową.
112. Jaką rolę pełni metoda `__init__()` w klasie?

Python

__init__ to inicjalizator instancji: jest wywoływany po utworzeniu obiektu i ustawia stan początkowy.

class Account:
    def __init__(self, owner: str, balance: float = 0.0) -> None:
        self.owner = owner
        self.balance = balance

W skrócie:

  • __init__ ustawia początkowe atrybuty obiektu.
  • Działa jako punkt wejścia konfiguracji instancji.
  • Nie tworzy obiektu, tylko go inicjalizuje.
113. Do czego służy parametr `self` w metodach klas Pythona?

Python

self to referencja do bieżącej instancji. Przez self metoda odczytuje i zmienia atrybuty instancji.

def deposit(self, amount: float) -> None:
    self.balance += amount

Nazwa self nie jest zarezerwowana składniowo, ale jest powszechnie przyjętym standardem.

W skrócie:

  • self wiąże metodę z konkretnym obiektem.
  • Przez self dostępne są dane instancji.
  • To obowiązkowy pierwszy parametr metody instancji.
114. Jak definiować metody w klasie?

Python

Metody definiuje się jako funkcje wewnątrz class.

class Calculator:
    def add(self, a: int, b: int) -> int:
        return a + b

Typowe rodzaje to metody instancji (self), klasy (@classmethod, cls) i statyczne (@staticmethod, bez self/cls).

W skrócie:

  • Metoda to funkcja wewnątrz ciała klasy.
  • Typ metody określają dekorator i sygnatura.
  • Najczęściej używana jest metoda instancji.
115. Wyjaśnij koncepcję "wszystko jest obiektem" w Pythonie i podaj przykłady.

Python

W Pythonie prawie wszystko jest obiektem: liczby, napisy, funkcje, klasy i moduły. Dlatego wszystko ma typ, atrybuty i zachowanie.

type(10)          # <class 'int'>
type(len)         # <class 'builtin_function_or_method'>
type(str)         # <class 'type'>

W skrócie:

  • Jednolity model obiektowy upraszcza język.
  • Funkcje i klasy też są obiektami pierwszej kategorii.
  • To sprawia, że Python jest elastyczny w metaprogramowaniu.
116. Podaj przykład klasy z metodami, które wykonują obliczenia na podstawie atrybutów.

Python

class Rectangle:
    def __init__(self, width: float, height: float) -> None:
        self.width = width
        self.height = height

    def area(self) -> float:
        return self.width * self.height

    def perimeter(self) -> float:
        return 2 * (self.width + self.height)

W skrócie:

  • Metody mogą obliczać wartości na podstawie atrybutów instancji.
  • To enkapsuluje logikę domenową w obiekcie.
  • API klasy staje się samoopisowe.
117. Opisz sytuację, w której może być potrzebne użycie kilku klas w programie Pythona.

Python

Gdy domena ma kilka odpowiedzialności, rozdziela się je między klasy. Na przykład w systemie e-commerce: Order, OrderItem, PaymentService, InventoryService.

To daje:

  • wyraźny podział odpowiedzialności;
  • słabsze sprzężenie;
  • łatwiejszą wymianę i testowanie komponentów.

W skrócie:

  • Kilka klas jest potrzebnych do modelowania złożonej domeny.
  • Podział odpowiedzialności zwiększa utrzymywalność.
  • Kompozycja klas zwykle jest lepsza niż "obiekt-bóg".
118. Jaka jest różnica między metodą instancji, metodą klasy i metodą statyczną?

Python

  • Metoda instancji ma self i działa na konkretnej instancji.
  • Metoda klasy ma cls i działa na klasie jako całości.
  • Metoda statyczna nie ma self/cls; to funkcja pomocnicza w przestrzeni nazw klasy.

W skrócie:

  • Metoda instancji to logika instancji.
  • Metoda klasy to logika klasy i alternatywne konstruktory.
  • Metoda statyczna to logika pomocnicza bez dostępu do stanu.
119. Czym jest `@classmethod` w Pythonie i czym różni się od zwykłych metod?

Python

@classmethod przekazuje jako pierwszy argument klasę cls, a nie instancję. Często używa się go do metod fabrykujących.

class User:
    def __init__(self, name: str) -> None:
        self.name = name

    @classmethod
    def from_email(cls, email: str) -> User:
        return cls(email.split("@")[0])

W skrócie:

  • classmethod działa na poziomie klasy.
  • Jest wygodny dla alternatywnych konstruktorów.
  • Nie wymaga już utworzonej instancji.
120. Czym jest `@staticmethod` w klasach Pythona i kiedy warto go stosować?

Python

@staticmethod definiuje metodę bez automatycznego self ani cls. Logicznie należy do klasy, ale nie zależy od jej stanu.

class Math:
    @staticmethod
    def clamp(value: int, min_v: int, max_v: int) -> int:
        return max(min_v, min(value, max_v))

W skrócie:

  • staticmethod to funkcja w namespace klasy.
  • Używaj go do logiki pomocniczej bez dostępu do atrybutów.
  • Nie nadaje się tam, gdzie potrzebny jest stan instancji albo klasy.
121. Jaka jest różnica między `@classmethod` a `@staticmethod` w klasach Pythona?

Python

Różnica dotyczy pierwszego argumentu i poziomu dostępu:

  • @classmethod otrzymuje cls i może pracować ze stanem klasy;
  • @staticmethod nie otrzymuje nic automatycznie.

Metoda klasowa częściej służy do metod fabrykujących i polimorficznych konstruktorów, a metoda statyczna do funkcji pomocniczych.

W skrócie:

  • Metoda klasowa "wie" o klasie.
  • Metoda statyczna jest odizolowana od stanu klasy i instancji.
  • Wybór zależy od tego, czy potrzebny jest dostęp do cls.
122. Czym są atrybuty instancji w klasach Pythona i czym różnią się od atrybutów klasowych?

Python

Atrybuty instancji należą do konkretnego obiektu, na przykład self.x. Atrybuty klasowe należą do klasy i są współdzielone przez wszystkie instancje.

class User:
    role = "member"           # atrybut klasowy
    def __init__(self, name: str) -> None:
        self.name = name      # atrybut instancji

W skrócie:

  • Atrybuty instancji przechowują indywidualny stan.
  • Atrybuty klasowe przechowują wspólną konfigurację.
  • Zmiany atrybutów klasowych wpływają na wszystkie instancje.
123. Czym jest `__slots__` w Pythonie?

Python

__slots__ ogranicza zestaw dozwolonych atrybutów w klasie i może zmniejszyć zużycie pamięci, usuwając __dict__ z instancji.

class Point:
    __slots__ = ("x", "y")
    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y

Niuanse użycia:

  • Oszczędność pamięci: obiekty zajmują znacznie mniej miejsca, bo atrybuty są przechowywane w stałej strukturze zamiast w tablicy haszującej __dict__.
  • Szybkość: dostęp do atrybutów w __slots__ bywa zwykle trochę szybszy.
  • Brak __dict__: nie da się dynamicznie dodawać nowych atrybutów, których nie ma na liście __slots__, chyba że doda się tam "__dict__".
  • Słabe referencje: jeśli chcesz używać weakref, trzeba jawnie dodać "__weakref__" do __slots__.

W skrócie:

  • __slots__ jest przydatny dla milionów lekkich obiektów, na przykład węzłów grafu.
  • Domyślnie usuwa __dict__ i __weakref__.
  • Zmniejsza elastyczność na rzecz wydajności i kontroli.
124. Czym są magic methods, czyli metody dunder, w klasach Pythona i dlaczego nazywa się je "magicznymi"?

Python

Metody dunder, takie jak __init__, __str__, __len__, __eq__, to specjalne punkty zaczepienia, które Python wywołuje automatycznie w odpowiedzi na operatory i funkcje wbudowane.

Nazywa się je "magicznymi", ponieważ integrują klasę z zachowaniem samego języka.

W skrócie:

  • Metody dunder definiują protokołowe zachowanie obiektu.
  • Pozwalają klasom zachowywać się "jak typy wbudowane".
  • Używaj ich tylko wtedy, gdy istnieje wyraźna potrzeba semantyczna.
125. Do czego służy metoda `__del__`?

Python

__del__ to finalizator, który może zostać wywołany przed zniszczeniem obiektu przez GC. Jego działanie nie jest deterministyczne, dlatego do zarządzania zasobami lepiej używać menedżerów kontekstu, czyli with, oraz jawnego zamykania.

W skrócie:

  • __del__ nie gwarantuje terminowego wykonania.
  • Nie opieraj krytycznego czyszczenia wyłącznie na nim.
  • Zalecane podejście to with lub try/finally.
126. Podaj przykład użycia magicznej metody `__str__` do zdefiniowania tekstowej reprezentacji własnej klasy w Pythonie.

Python

class User:
    def __init__(self, name: str, active: bool) -> None:
        self.name = name
        self.active = active

    def __str__(self) -> str:
        status = "active" if self.active else "inactive"
        return f"User(name={self.name}, status={status})"

str(user) i print(user) użyją __str__.

W skrócie:

  • __str__ daje reprezentację obiektu zrozumiałą dla człowieka.
  • Jest przydatny w logach i wyjściu CLI.
  • Powinien być krótki i czytelny.
127. Do czego używa się `__str__` i `__repr__`?

Python

__str__ jest skierowany do końcowego odbiorcy. __repr__ jest skierowany do programisty i debugowania, dlatego najlepiej, by był jednoznaczny.

print(obj) zwykle używa __str__, a REPL oraz repr(obj) używają __repr__.

W skrócie:

  • __str__ służy do przyjaznego wyjścia.
  • __repr__ służy do technicznej reprezentacji.
  • Dobrą praktyką jest posiadanie obu, jeśli klasa jest domenowa.
128. Jak działa przeciążanie operatorów w Pythonie i dlaczego jest to przydatne?

Python

Przeciążanie operatorów polega na implementacji metod dunder operatorów, takich jak __add__, __sub__, __eq__, aby obiekty własnej klasy obsługiwały operatory.

class Vector2:
    def __init__(self, x: float, y: float) -> None:
        self.x = x
        self.y = y

    def __add__(self, other: "Vector2") -> "Vector2":
        return Vector2(self.x + other.x, self.y + other.y)

W skrócie:

  • Daje naturalną składnię dla typów domenowych.
  • Zwiększa wyrazistość API.
  • Ważne jest zachowanie matematycznie oczekiwanej semantyki.
129. Czym jest dziedziczenie?

Python

Dziedziczenie pozwala utworzyć klasę pochodną, która odziedzicza atrybuty i metody klasy bazowej oraz może rozszerzać albo nadpisywać jej zachowanie.

W skrócie:

  • Dziedziczenie sprzyja ponownemu użyciu kodu.
  • Klasa pochodna może nadpisywać metody klasy bazowej.
  • Nadmierna hierarchia utrudnia utrzymanie, więc często lepsza jest kompozycja.
130. Czym jest pojedyncze dziedziczenie w Pythonie?

Python

Pojedyncze dziedziczenie oznacza, że klasa ma tylko jednego bezpośredniego rodzica.

class Animal:
    ...

class Dog(Animal):
    ...

To najprostsza i zwykle najbardziej czytelna forma dziedziczenia.

W skrócie:

  • Jedna klasa pochodna oznacza jedną klasę bazową.
  • To prosty model rozwiązywania metod.
  • Często wystarcza dla większości modeli biznesowych.
131. Jak zaimplementować dziedziczenie w klasach Pythona i jaka składnia jest do tego używana?

Python

Składnia: class Child(Base):.

class Base:
    def greet(self) -> str:
        return "hello"

class Child(Base):
    def greet(self) -> str:
        return "hi"

Jeśli trzeba wywołać logikę klasy bazowej, użyj super().

W skrócie:

  • Dziedziczenie zapisuje się w nawiasach po nazwie klasy.
  • Klasa pochodna otrzymuje API klasy bazowej.
  • Nadpisywanie pozwala dostosować zachowanie.
132. Jak uzyskać dostęp do składowych klasy bazowej w klasie pochodnej?

Python

Dostęp jest możliwy bezpośrednio przez odziedziczone atrybuty i metody albo przez super().

class Base:
    def greet(self) -> str:
        return "hello"

class Child(Base):
    def greet(self) -> str:
        return super().greet() + " world"

W skrócie:

  • Odziedziczone składowe są dostępne w klasie pochodnej automatycznie.
  • super() poprawnie wywołuje logikę klasy bazowej.
  • To ważne przy rozszerzaniu, a nie duplikowaniu zachowania.
133. Do czego służy funkcja `super()` w dziedziczeniu w Pythonie i jak się jej używa?

Python

super() zwraca obiekt pośredniczący do następnej klasy zgodnie z MRO, aby wywołać jej metody. Typowo używa się jej w __init__ oraz przy kooperatywnym wielokrotnym dziedziczeniu.

class Child(Base):
    def __init__(self, value: int) -> None:
        super().__init__()
        self.value = value

W skrócie:

  • super() wywołuje implementację klasy bazowej bez twardego wskazywania jej nazwy.
  • Pomaga zachować poprawność względem MRO.
  • To zalecany sposób pracy z dziedziczeniem.
134. Opisz funkcję `isinstance()` w Pythonie i podaj przykład jej użycia.

Python

isinstance(obj, cls_or_tuple) sprawdza, czy obiekt należy do danego typu albo jego podklasy.

isinstance(10, int)                 # True
isinstance(True, int)               # True
isinstance("x", (str, bytes))       # True

W skrócie:

  • isinstance jest bezpieczniejsze niż type(obj) is ... w kodzie polimorficznym.
  • Uwzględnia hierarchię dziedziczenia.
  • Obsługuje krotkę typów.
135. Wyjaśnij funkcję `issubclass()` w Pythonie i podaj przykład jej zastosowania.

Python

issubclass(Sub, Base) sprawdza, czy klasa Sub jest podklasą Base.

class Animal: ...
class Dog(Animal): ...

issubclass(Dog, Animal)  # True

W skrócie:

  • Działa na klasach, a nie na instancjach.
  • Jest wygodna do walidacji API na poziomie typów.
  • Uwzględnia dziedziczenie tranzytywne.
136. Czym jest wielokrotne dziedziczenie?

Python

Wielokrotne dziedziczenie oznacza dziedziczenie jednej klasy po kilku klasach bazowych.

class A: ...
class B: ...
class C(A, B): ...

Daje elastyczność, ale wymaga dyscypliny w projektowaniu metod i używaniu super().

W skrócie:

  • Jedna klasa może dziedziczyć zachowanie z kilku źródeł.
  • Jest przydatne w podejściu opartym na mixinach.
  • Może utrudniać zrozumienie MRO.
137. Jak działa MRO (Method Resolution Order) przy wielokrotnym dziedziczeniu?

Python

MRO określa kolejność wyszukiwania metod w hierarchii klas. W Pythonie używany jest algorytm linearyzacji C3.

Problem diamentu: To klasyczny przypadek, gdy klasa D dziedziczy po B i C, a obie te klasy dziedziczą po A. MRO gwarantuje, że A zostanie rozpatrzona dopiero po wszystkich jej potomkach, czyli B i C.

class A: pass
class B(A): pass
class C(A): pass
class D(B, C): pass

print(D.mro())
# [D, B, C, A, object]

Kolejność można sprawdzić przez ClassName.__mro__ albo ClassName.mro().

W skrócie:

  • MRO decyduje, z której klasy bazowej pobrać metodę.
  • Kolejność jest przewidywalna i formalnie określona przez C3.
  • Dzięki MRO Python poprawnie obsługuje problem diamentu.
  • Przy kooperatywnym dziedziczeniu wszystkie klasy powinny wywoływać super().
138. Jakie są zalety i wady używania wielokrotnego dziedziczenia?

Python

Zalety:

  • ponowne użycie zachowania z kilku źródeł;
  • wygodne mixiny do "dodawania" możliwości.

Wady:

  • bardziej złożone MRO;
  • ryzyko konfliktów nazw i zachowań;
  • trudniejsze debugowanie i wdrożenie w temat.

W skrócie:

  • Wielokrotne dziedziczenie jest potężne, ale wymaga ścisłych zasad projektowych.
  • W większości przypadków kompozycja jest prostsza.
  • Używaj go głównie dla niewielkich mixinów.
139. Czym są mixiny?

Python

Mixin to niewielka klasa z wąskim dodatkowym zachowaniem, przeznaczona do łączenia przez dziedziczenie, a nie do samodzielnego użycia.

Przykłady: TimestampMixin, JsonSerializableMixin.

W skrócie:

  • Mixin dodaje jedną konkretną możliwość.
  • Zwykle nie ma własnego pełnego cyklu życia.
  • Dobrze łączy się z wielokrotnym dziedziczeniem.
140. Czym jest enkapsulacja w Pythonie?

Python

Enkapsulacja to ukrywanie wewnętrznej implementacji za stabilnym publicznym API. W Pythonie realizuje się to głównie przez konwencje i property, a nie przez sztywne modyfikatory dostępu.

W skrócie:

  • Kod kliencki pracuje z interfejsem, a nie z detalami.
  • Enkapsulacja zmniejsza sprzężenie między komponentami.
  • Ułatwia zmianę wewnętrznej implementacji bez psucia API.
141. Jaka jest różnica między dostępem publicznym, prywatnym i chronionym?

Python

W Pythonie są to głównie konwencje nazewnicze:

  • public: name — dostępny wszędzie;
  • protected: _name — do użytku wewnętrznego zgodnie z konwencją;
  • private: __name — powoduje zmianę nazwy, czyli _ClassName__name, ale nie daje absolutnej ochrony.

W skrócie:

  • Python nie ma ścisłych modyfikatorów dostępu jak Java czy C#.
  • _name i __name są sygnałami intencji dla programistów.
  • Realna kontrola dostępu wynika z projektu API.
142. Czym jest polimorfizm i jak jest realizowany w Pythonie?

Python

Polimorfizm to możliwość pracy z różnymi obiektami przez wspólny interfejs. W Pythonie często realizuje się go przez typowanie oparte na zachowaniu i protokoły.

def render(obj) -> str:
    return obj.to_text()

Każdy obiekt z metodą to_text będzie odpowiedni.

W skrócie:

  • Jeden interfejs, wiele implementacji.
  • W Pythonie polimorfizm jest często "behawioralny", a nie hierarchiczny.
  • To upraszcza rozszerzanie systemu o nowe typy.
143. Czym jest abstrakcja w Pythonie?

Python

Abstrakcja to wyodrębnienie istotnego API i ukrycie zbędnych szczegółów implementacji. Klient pracuje z kontraktem, a nie z wewnętrznymi krokami.

W skrócie:

  • Abstrakcja zmniejsza obciążenie poznawcze.
  • Ułatwia wymianę implementacji bez zmian w kodzie klienckim.
  • Jest realizowana przez interfejsy, ABC, protokoły i fasady.
144. Jak zaimplementować abstrakcję danych?

Python

Podejście:

  • ukryć bezpośredni dostęp do pól wewnętrznych, na przykład _field;
  • udostępnić kontrolowane API przez metody albo @property;
  • walidować inwarianty w logice settera.
class Temperature:
    def __init__(self, celsius: float) -> None:
        self.celsius = celsius

    @property
    def celsius(self) -> float:
        return self._celsius

    @celsius.setter
    def celsius(self, value: float) -> None:
        if value < -273.15:
            raise ValueError("invalid temperature")
        self._celsius = value

W skrócie:

  • Abstrakcja danych chroni inwarianty obiektu.
  • property daje kontrolowany dostęp do stanu.
  • Szczegóły wewnętrzne można zmieniać bez zmiany API.
145. Czym są ABC i `@abstractmethod`?

Python

ABC, czyli Abstract Base Class, to abstrakcyjna klasa bazowa z modułu abc. @abstractmethod oznacza metodę, którą podklasa musi obowiązkowo zaimplementować.

from abc import ABC, abstractmethod

class Storage(ABC):
    @abstractmethod
    def save(self, data: bytes) -> None:
        ...

W skrócie:

  • ABC formalizuje kontrakt hierarchii.
  • @abstractmethod uniemożliwia utworzenie instancji niepełnej implementacji.
  • To przydatne w architekturach wtyczkowych i rozszerzalnych.
146. Czym jest property w Pythonie i jak się go używa?

Python

property zamienia metody dostępowe w API przypominające atrybut, z możliwością walidacji, obliczeń albo logiki leniwej.

class User:
    def __init__(self, name: str) -> None:
        self._name = name

    @property
    def name(self) -> str:
        return self._name

W skrócie:

  • property daje kontrolę dostępu bez zmiany zewnętrznej składni.
  • Nadaje się do walidacji i wartości pochodnych.
  • Pozwala rozwijać API bez psucia kodu klientów.
147. Czym jest `@property`?

Python

@property to dekorator dla metody getter. Razem z @x.setter i @x.deleter tworzy kontrolowany atrybut.

W skrócie:

  • @property pozwala odczytywać metodę jak pole.
  • Pomaga enkapsulować wewnętrzną implementację.
  • Często jest używane dla API zachowującego kompatybilność wsteczną.
148. Czym jest deskryptor w Pythonie?

Python

Deskryptor to obiekt, który implementuje __get__, __set__ albo __delete__ i steruje dostępem do atrybutów innej klasy.

Na deskryptorach opierają się property, classmethod i staticmethod.

W skrócie:

  • Deskryptor to niskopoziomowy mechanizm dostępu do atrybutów.
  • Pozwala wielokrotnie używać logiki walidacji i pośredniczenia pól.
  • To podstawa wielu technik metaprogramistycznych w Pythonie.
149. Czym różnią się property i deskryptor?

Python

property to gotowy wysokopoziomowy deskryptor dla jednego atrybutu. Własny deskryptor to bardziej ogólny mechanizm, który można wielokrotnie wykorzystać w wielu polach i klasach.

W skrócie:

  • property jest prostsze i lokalne.
  • Deskryptor jest bardziej elastyczny i skalowalny.
  • property jest faktycznie zbudowane na protokole deskryptora.
150. Kiedy lepiej używać property, a kiedy deskryptora?

Python

Używaj property, gdy logika dotyczy jednego lub dwóch pól konkretnej klasy. Używaj deskryptora, gdy tę samą logikę, na przykład walidację, rzutowanie czy leniwą inicjalizację, trzeba wielokrotnie zastosować w wielu klasach.

W skrócie:

  • Lokalna logika pola oznacza property.
  • Wielokrotne użycie polityki dostępu oznacza deskryptor.
  • Deskryptor opłaca się w dużych modelach domenowych.
151. Do czego używa się `setattr()`, `getattr()` i `hasattr()`? Jaka jest między nimi różnica?

Python

To funkcje dynamicznego dostępu do atrybutów:

  • getattr(obj, name[, default]) — odczytać atrybut;
  • setattr(obj, name, value) — ustawić atrybut;
  • hasattr(obj, name) — sprawdzić, czy istnieje.
value = getattr(user, "email", None)
if not hasattr(user, "active"):
    setattr(user, "active", True)

W skrócie:

  • getattr/setattr/hasattr służą do dynamicznej pracy z obiektami.
  • Są przydatne w kodzie generycznym, serializacji i adapterach.
  • Nie warto ich nadużywać, żeby nie stracić czytelności.
152. Wyjaśnij znaczenie metody `__set_name__` w deskryptorach Pythona i podaj przykład jej zastosowania.

Python

__set_name__(self, owner, name) jest wywoływana podczas tworzenia klasy i przekazuje deskryptorowi nazwę atrybutu, do którego został przypisany.

class Field:
    def __set_name__(self, owner, name): self.name = name

W skrócie:

  • __set_name__ inicjalizuje deskryptor kontekstem klasy.
  • Pozwala tworzyć wielokrotnie używalne walidatory pól.
  • Uruchamia się raz na etapie tworzenia klasy.
153. Czym jest `dataclass` i kiedy warto go używać?

Python

@dataclass automatycznie generuje powtarzalny kod, taki jak __init__, __repr__, __eq__. Nadaje się do modeli danych bez złożonego zachowania.

from dataclasses import dataclass
@dataclass(slots=True)
class User:
    name: str
    active: bool = True

W skrócie:

  • dataclass skraca kod dla modeli danych.
  • To dobry wybór dla DTO i konfiguracji.
  • Przy złożonej walidacji często potrzebne są inne narzędzia.
154. Jaka jest różnica między `dataclass` a Pydantic?

Python

dataclass koncentruje się na wygodnym opisie struktury. Pydantic dodaje walidację w czasie działania, parsowanie i serializację danych.

W skrócie:

  • dataclass jest lżejszy i szybszy dla modeli wewnętrznych.
  • Pydantic lepiej nadaje się do danych wejściowych i API.
  • Wybór zależy od potrzeby walidacji podczas działania programu.
155. Czym są podpowiedzi typów i po co są potrzebne?

Python

Podpowiedzi typów to adnotacje typów w sygnaturach i zmiennych, które tworzą jawny kontrakt. Poprawiają podpowiedzi IDE i analizę statyczną.

W skrócie:

  • Podpowiedzi typów zwiększają zrozumiałość API.
  • Pozwalają wcześniej wykrywać pewną klasę błędów.
  • Są przydatne nawet w języku dynamicznym.
156. Jak działa statyczne sprawdzanie typów w `mypy`?

Python

mypy odczytuje adnotacje typów i analizuje kod bez uruchamiania, sprawdzając zgodność typów. Dzięki temu wychwytuje błędy integracyjne przed wykonaniem programu.

W skrócie:

  • mypy wykonuje sprawdzanie podobne do kompilacji dla Pythona.
  • Najlepiej sprawdza się w CI.
  • Tryby ścisłe zmniejszają liczbę błędów produkcyjnych.
157. Jak zapewnić bezpieczeństwo typów w projekcie Pythona?

Python

  • konsekwentnie dodawać adnotacje typów do publicznego API;
  • włączyć mypy albo pyright w CI;
  • używać TypedDict, Protocol i generyków;
  • minimalizować Any i niejawne rzutowania.

W skrócie:

  • Bezpieczeństwo typów to proces, a nie jednorazowe działanie.
  • Największy efekt daje bramka CI dla typów.
  • Stopniowe zwiększanie rygoru działa lepiej niż podejście "big bang".
158. Czym jest `TypedDict`?

Python

TypedDict opisuje typowaną formę słownika: jakie klucze są oczekiwane i jakich typów są ich wartości.

from typing import TypedDict
class UserPayload(TypedDict): name: str; active: bool

W skrócie:

  • TypedDict typuje dict o stałych kluczach.
  • Jest wygodny dla struktur podobnych do JSON.
  • Jest sprawdzany przez analizator statyczny.
159. Czym jest `Protocol` w typowaniu?

Python

Protocol opisuje kontrakt behawioralny, czyli typowanie strukturalne: typ jest zgodny, jeśli ma wymagane metody i atrybuty, niezależnie od dziedziczenia.

W skrócie:

  • Protocol realizuje typowanie oparte na zachowaniu w statycznym typowaniu.
  • Zmniejsza silne powiązanie z konkretnymi klasami.
  • Jest przydatny dla testowalnych i rozszerzalnych API.
160. Czym są generyki w Pythonie?

Python

Generyki pozwalają pisać typy i funkcje parametryzowane innymi typami. We współczesnej składni: class Box[T]: ..., def first[T](...) -> T.

W skrócie:

  • Generyki sprawiają, że typy stają się wielokrotnego użytku.
  • Wzmacniają bezpieczeństwo typów kolekcji i kontenerów.
  • Zmniejszają duplikację typowanego kodu.
161. Czym jest Pydantic i do czego jest używany?

Python

Pydantic to biblioteka do opisywania schematów danych z walidacją w czasie działania, konwersją typów i wygodną serializacją.

Typowe zastosowania to modele żądania i odpowiedzi w FastAPI oraz konfiguracja aplikacji.

W skrócie:

  • Pydantic waliduje dane zewnętrzne podczas działania programu.
  • Jest wygodny dla API i integracji.
  • Daje czytelne błędy walidacji i schematy danych.
162. Czym są wyjątki w Pythonie?

Python

Wyjątek to obiekt sygnalizujący błędną albo wyjątkową sytuację podczas wykonywania kodu.

W skrócie:

  • Wyjątki przerywają normalny przepływ wykonania.
  • Należy je obsługiwać tam, gdzie można podjąć decyzję.
  • Poprawny model błędów zwiększa niezawodność systemu.
163. Jakie są trzy typy błędów w Pythonie i czym się różnią?

Python

  • błędy składniowe: pojawiają się przed uruchomieniem;
  • wyjątki czasu wykonania: pojawiają się w trakcie działania;
  • błędy logiczne: kod działa, ale zwraca niepoprawny wynik.

W skrócie:

  • Błędy składniowe i część błędów wykonania wychwytuje interpreter.
  • Błędy logiczne wykrywa się testami i przeglądem kodu.
  • Każdy typ wymaga innego podejścia diagnostycznego.
164. Jak używać `try`, `except`, `else` i `finally`?

Python

try zawiera ryzykowną operację, except obsługuje błąd, else wykonuje się, gdy błędu nie było, a finally wykonuje się zawsze, zwykle przy czyszczeniu zasobów.

try: data = load()
except FileNotFoundError: data = {}
else: validate(data)
finally: close_connections()

W skrócie:

  • else warto zostawić dla kodu "ścieżki sukcesu".
  • finally służy do zasobów i czyszczenia.
  • Łap konkretne wyjątki, a nie "wszystko po kolei".
165. Co oznacza kolejność bloków `except`?

Python

Bloki except są sprawdzane od góry do dołu, dlatego najpierw umieszcza się najbardziej konkretne wyjątki, a ogólne, takie jak Exception, na końcu.

W skrócie:

  • Kolejność except wpływa na to, który obsługujący blok zostanie uruchomiony.
  • Szeroki except na górze "połyka" przypadki specyficzne.
  • To krytyczne dla poprawnego przepływu odzyskiwania po błędzie.
166. Jakie jest przeznaczenie słowa kluczowego `assert` w kodzie Pythona?

Python

assert sprawdza inwariant i zgłasza AssertionError, jeśli warunek jest fałszywy. Nadaje się do wewnętrznych kontroli deweloperskich, a nie do walidacji danych wejściowych użytkownika.

W skrócie:

  • assert dokumentuje, że "to powinno być prawdziwe".
  • Nie zastępuje produkcyjnej obsługi błędów.
  • Używaj go do kontraktów w logice wewnętrznej.
167. Czym `raise` różni się od zwykłego wypisania komunikatu o błędzie w Pythonie?

Python

raise zmienia przepływ sterowania i sygnalizuje błąd kodowi wywołującemu. print tylko wypisuje tekst i nie zatrzymuje błędnego scenariusza.

W skrócie:

  • raise jest mechanizmem obsługi błędów, a print nie.
  • Wyjątki można centralnie przechwytywać i logować.
  • print służy do diagnostyki, a nie do kontraktu błędów.
168. Jak utworzyć własny wyjątek?

Python

Utwórz klasę dziedziczącą po Exception albo po bardziej konkretnym wyjątku bazowym.

class InvalidOrderError(Exception):
    pass

W skrócie:

  • Własny wyjątek czyni błędy bardziej wyrazistymi domenowo.
  • Pozwala precyzyjnie przechwytywać potrzebne przypadki.
  • Często jest lepszy niż używanie wszędzie ogólnego ValueError.
169. Jakie znaczenie ma tworzenie własnych wyjątków w Pythonie i jak poprawiają one obsługę błędów?

Python

Własne wyjątki budują jawny model błędów domeny i oddzielają problemy techniczne od reguł biznesowych.

W skrócie:

  • Poprawiają czytelność i utrzymanie obsługi błędów.
  • Dają precyzyjniejszą semantykę w logach i API.
  • Ułatwiają testowanie scenariuszy negatywnych.
170. Jak są zorganizowane wyjątki w Pythonie i jaka jest hierarchia klas wyjątków?

Python

Hierarchia jest zbudowana od BaseException. W kodzie aplikacyjnym prawie zawsze pracuje się z potomkami Exception.

Kluczowe gałęzie to ValueError, TypeError, KeyError, OSError, RuntimeError.

W skrócie:

  • Wyjątki tworzą drzewo klas.
  • Lepiej przechwytywać konkretne podklasy.
  • BaseException zwykle nie jest przechwytywany w logice biznesowej.
171. Jakie są podejścia do obsługi kilku różnych wyjątków w Pythonie i dlaczego warto obsługiwać je osobno?

Python

Podejścia:

  • osobne except dla każdego typu;
  • grupowanie logicznie podobnych przypadków: except (A, B):;
  • osobne strategie odzyskiwania dla każdej kategorii.

W skrócie:

  • Różne wyjątki często wymagają różnych działań.
  • Osobna obsługa zmniejsza ryzyko ukrytych defektów.
  • Logi stają się dokładniejsze i bardziej użyteczne.
172. Po co ponownie zgłaszać wyjątek w Pythonie i kiedy jest to przydatne?

Python

Ponowne zgłoszenie wyjątku, czyli raise bez argumentów w except, pozwala dodać kontekst, na przykład logi, metryki albo czyszczenie, i przekazać ten sam błąd wyżej.

W skrócie:

  • Ponowne zgłoszenie zachowuje pierwotny ślad stosu.
  • Jest przydatne przy scentralizowanej obsłudze na wyższym poziomie.
  • Nie "połykaj" krytycznych błędów bez potrzeby.
173. Dlaczego użycie bloku try-except warto ograniczać w programach Pythona i jak wpływa to na wydajność?

Python

try/except jest potrzebne, ale nie powinno obejmować dużych bloków "na wszelki wypadek". Jest to szczególnie kosztowne, gdy wyjątki pojawiają się często i sterują przepływem programu.

W skrócie:

  • Przechwytuj tylko oczekiwane błędy w możliwie wąskim miejscu.
  • Częste wyjątki pogarszają wydajność i czytelność.
  • Tam, gdzie to ma sens, lepsze są jawne sprawdzenia.
174. Jak obsługiwać błędy podczas pracy z plikami?

Python

Używaj with i przechwytuj konkretne wyjątki, takie jak FileNotFoundError, PermissionError, UnicodeDecodeError i OSError.

try:
    with open(path, "r", encoding="utf-8") as f:
        content = f.read()
except FileNotFoundError:
    ...

W skrócie:

  • with gwarantuje zamknięcie pliku.
  • Obsługuj konkretne błędy wejścia i wyjścia.
  • Loguj kontekst, taki jak ścieżka, tryb i kodowanie.
175. Jakie są tryby pracy z plikami w Pythonie?

Python

Podstawowe tryby:

  • r odczyt;
  • w nadpisanie;
  • a dopisywanie na końcu;
  • x utworzenie nowego pliku;
  • b tryb binarny;
  • t tryb tekstowy, domyślny;
  • + odczyt i zapis.

W skrócie:

  • Tryb określa semantykę bezpieczeństwa i zmian w pliku.
  • w usuwa poprzednią zawartość, a a ją zachowuje.
  • Dla danych bajtowych używaj b.
176. Dlaczego ważne jest zamykanie plików po operacjach i co może się stać, jeśli plik pozostanie otwarty?

Python

Otwarty plik utrzymuje deskryptor systemu operacyjnego. Jeśli go nie zamknąć, mogą wystąpić:

  • wyciek deskryptorów plików;
  • blokady plików lub niepełne opróżnienie bufora;
  • niestabilność w długotrwałych procesach.

W skrócie:

  • Zamknięcie pliku zwalnia zasoby systemu operacyjnego.
  • with automatyzuje bezpieczne zamykanie.
  • To krytyczne dla usług i skryptów wsadowych.
177. Jaka jest różnica między metodami `read()`, `readline()` i `readlines()` przy odczycie plików?

Python

  • read() czyta cały plik albo n bajtów lub znaków;
  • readline() czyta jeden wiersz;
  • readlines() czyta wszystkie wiersze do listy.

W skrócie:

  • read() i readlines() mogą być kosztowne pamięciowo.
  • Dla dużych plików lepsza jest iteracja for line in file.
  • Wybór zależy od rozmiaru i scenariusza przetwarzania.
178. Jak zapisać dane do pliku w Pythonie i jaka jest różnica między trybem `w` a `a`?

Python

Zapis:

with open("out.txt", "w", encoding="utf-8") as f:
    f.write("hello\n")

w nadpisuje plik od początku, a a dopisuje nową treść na końcu.

W skrócie:

  • w usuwa stare dane.
  • a zachowuje istniejącą zawartość.
  • Do logów zwykle używa się a.
179. Jak efektywnie pracować z dużymi plikami?

Python

  • czytać strumieniowo, po wierszach albo blokach;
  • unikać ładowania całego pliku do pamięci;
  • używać buforowania i generatorów;
  • dla danych kolumnowych i tabelarycznych dobierać odpowiednie formaty i parsery.

W skrócie:

  • Klucz to przetwarzanie strumieniowe zamiast pełnego odczytu.
  • Generatory zmniejszają zużycie pamięci.
  • Algorytm przetwarzania jest ważniejszy niż mikrooptymalizacje.
180. Czym są menedżery kontekstu?

Python

Menedżer kontekstu zarządza zasobem przez protokół __enter__/__exit__: otwarcie lub inicjalizację oraz gwarantowane zakończenie i czyszczenie.

W skrócie:

  • Zapewnia bezpieczny cykl życia zasobu.
  • Działa przez with.
  • Zmniejsza liczbę wycieków zasobów.
181. Jak działa konstrukcja `with`?

Python

with wywołuje __enter__ przy wejściu do bloku i __exit__ przy wyjściu, nawet jeśli wystąpił błąd.

with open("data.txt", "r", encoding="utf-8") as f:
    data = f.read()

W skrócie:

  • with gwarantuje czyszczenie zasobów.
  • Sprawia, że praca z zasobami jest deklaratywna.
  • Jest zalecany dla plików, blokad, transakcji i sesji.
182. Jak utworzyć własny menedżer kontekstu?

Python

Dwa podejścia:

  • klasa z __enter__ i __exit__;
  • funkcja z dekoratorem contextlib.contextmanager.
from contextlib import contextmanager

@contextmanager
def temp_flag():
    yield

W skrócie:

  • Klasa nadaje się do złożonego stanu.
  • contextmanager jest wygodny dla krótkich scenariuszy.
  • Oba podejścia dają gwarantowane czyszczenie.
183. Jak Python zarządza pamięcią?

Python

CPython używa zliczania referencji i cyklicznego mechanizmu odśmiecania pamięci. Dodatkowo ma wewnętrzny alokator pymalloc dla małych obiektów.

W skrócie:

  • Podstawowy mechanizm to licznik referencji.
  • GC usuwa cykliczne referencje.
  • Pamięć nie zawsze jest zwalniana natychmiast na poziomie systemu operacyjnego.
184. Czym jest zliczanie referencji w Pythonie i dlaczego jest ważne dla zarządzania pamięcią?

Python

Każdy obiekt ma licznik referencji. Gdy spada on do zera, obiekt można zwolnić. Zapewnia to szybkie usuwanie większości krótkotrwałych obiektów.

W skrócie:

  • Zliczanie referencji daje przewidywalny cykl życia obiektów.
  • Samodzielnie nie rozwiązuje problemu cyklicznych referencji.
  • Razem z GC tworzy pełny model zarządzania pamięcią.
185. Czym jest odśmiecanie pamięci w Pythonie?

Python

Odśmiecanie pamięci w CPython znajduje i usuwa nieosiągalne cykle obiektów, których nie da się wyczyścić wyłącznie przez zliczanie referencji.

W skrócie:

  • GC uzupełnia zliczanie referencji.
  • Jest szczególnie ważny dla cyklicznych grafów referencji.
  • Można nim sterować przez moduł gc.
186. Jak uzyskać adres pamięci obiektu w Pythonie?

Python

W CPython id(obj) często odpowiada adresowi obiektu w pamięci jako liczba całkowita. To narzędzie diagnostyczne, a nie stabilny identyfikator zewnętrzny.

W skrócie:

  • id() daje tożsamość obiektu.
  • W CPython zwykle jest to adres pamięci.
  • Nie używaj tego w logice biznesowej.
187. Do czego służy funkcja `getrefcount()` z modułu `sys` i jak działa w Pythonie?

Python

sys.getrefcount(obj) zwraca bieżący licznik referencji do obiektu w CPython. Wartość jest zwykle o 1 większa przez tymczasową referencję argumentu.

W skrócie:

  • To narzędzie diagnostyki zachowania pamięci.
  • Jest przydatne do analizy wycieków referencji.
  • Nie należy na nim polegać w logice aplikacyjnej.
188. Wyjaśnij na przykładach różnicę między obiektami mutable i immutable w kontekście zliczania referencji i zarządzania pamięcią.

Python

Obiekty immutable się nie zmieniają, więc ich "zmiana" tworzy nowy obiekt. Obiekty mutable zmieniają się in-place i wszystkie referencje widzą tę zmianę.

a = "x"; b = a; a += "y"    # nowy obiekt
x = [1]; y = x; y.append(2) # zmiana tego samego obiektu

W skrócie:

  • Obiekty immutable zmniejszają liczbę efektów ubocznych.
  • Obiekty mutable są bardziej efektywne przy modyfikacjach in-place.
  • Ta różnica jest krytyczna przy kopiowaniu i współdzielonych referencjach.
189. Jaka jest różnica między kopią płytką a kopią głęboką w Pythonie i kiedy używać każdego z tych podejść?

Python

Kopia płytka kopiuje tylko zewnętrzny kontener, a zagnieżdżone obiekty pozostają współdzielone. Kopia głęboka rekurencyjnie kopiuje całą strukturę.

import copy
copy.copy(obj)
copy.deepcopy(obj)

W skrócie:

  • Kopia płytka wystarcza dla "płaskich" struktur.
  • Kopia głęboka jest potrzebna do izolowanej pracy z zagnieżdżonymi danymi mutable.
  • Kopia głęboka jest droższa czasowo i pamięciowo.
190. Czym są moduły i pakiety w Pythonie?

Python

Moduł to osobny plik .py z kodem. Pakiet to katalog modułów, czyli przestrzeń nazw, która organizuje kod w większe bloki.

W skrócie:

  • Moduł to jednostka kodu.
  • Pakiet to struktura do skalowania modułów.
  • Podział na moduły i pakiety poprawia utrzymywalność.
191. Jak importować moduł w Pythonie?

Python

Podstawowe formy:

  • import module;
  • import module as m;
  • from module import name;
  • from package import submodule.

W skrócie:

  • Import ładuje moduł i udostępnia go w przestrzeni nazw.
  • Alias as poprawia czytelność i pomaga unikać konfliktów.
  • Warto preferować jawne importy.
192. Jakie są typy importów?

Python

Popularne typy:

  • bezwzględne;
  • względne;
  • wybiórcze, jak from x import y;
  • importy z aliasem przez as;
  • dynamiczne, z użyciem importlib.

W skrócie:

  • Typ importu wpływa na czytelność i utrzymanie.
  • W kodzie produkcyjnym najczęściej używa się importów bezwzględnych.
  • Importy dynamiczne stosuje się punktowo.
193. Jak działa system importów?

Python

Interpreter szuka modułu według sys.path, ładuje go tylko raz i zapisuje w sys.modules. Ponowny import korzysta z pamięci podręcznej.

W skrócie:

  • Import to wyszukiwanie, wykonanie modułu i zapisanie go w pamięci podręcznej.
  • To wyjaśnia, dlaczego kod modułu uruchamia się przy pierwszym imporcie.
  • Cykliczne importy wynikają z kolejności inicjalizacji modułów.
194. Jaka jest różnica między importem bezwzględnym a względnym?

Python

Import bezwzględny zaczyna się od korzenia pakietu, na przykład from app.utils import x. Import względny używa kropek, na przykład from .utils import x.

W skrócie:

  • Importy bezwzględne są czytelniejsze i stabilniejsze.
  • Względne bywają wygodne wewnątrz pakietu, ale gorzej znoszą refaktoryzację.
  • W dużych projektach lepiej standardowo trzymać się importów bezwzględnych.
195. Co robi funkcja `dir()` i jak można jej używać względem modułów?

Python

dir(obj) zwraca listę dostępnych atrybutów i nazw obiektu. W przypadku modułów to szybki sposób na podejrzenie API.

import math
dir(math)

W skrócie:

  • dir() pomaga w interaktywnym poznawaniu modułów.
  • Jest przydatne w REPL i podczas debugowania.
  • Nie zastępuje oficjalnej dokumentacji.
196. Wyjaśnij rolę konstrukcji `if __name__ == "__main__":` w skryptach Pythona.

Python

Ten blok wykonuje się tylko wtedy, gdy plik jest uruchamiany jako skrypt, a nie importowany jako moduł. Rozdziela to kod wielokrotnego użytku od punktu wejścia CLI.

W skrócie:

  • Pozwala moduł zarówno uruchamiać, jak i importować.
  • Zapobiega niechcianemu wykonaniu podczas importu.
  • To standardowy wzorzec dla skryptów.
197. Co oznacza plik `__init__.py` w pakiecie Pythona?

Python

__init__.py oznacza katalog jako pakiet i może inicjalizować API na poziomie pakietu, na przykład przez ponowny eksport klas i funkcji.

W skrócie:

  • Tworzy publiczny interfejs pakietu.
  • Może zawierać minimalną inicjalizację.
  • Nie warto przeciążać go ciężką logiką.
198. Jakie są najlepsze praktyki dotyczące stylu importów w kodzie Pythona?

Python

  • grupować importy: biblioteka standardowa, zewnętrzne i lokalne;
  • unikać from x import *;
  • trzymać importy na początku pliku, poza uzasadnionymi przypadkami leniwego importu;
  • używać sortowania i formatowania, na przykład ruff oraz isort.

W skrócie:

  • Spójne importy poprawiają czytelność.
  • Jawne importy zmniejszają konflikty nazw.
  • Automatyzacja narzędziami eliminuje ręczne błędy.
199. Jakie popularne moduły wbudowane istnieją w Pythonie?

Python

Przykłady popularnych modułów biblioteki standardowej:

  • os, sys, pathlib, json, datetime, re,
  • collections, itertools, functools, typing,
  • asyncio, subprocess, logging, argparse.

W skrócie:

  • Biblioteka standardowa pokrywa większość podstawowych zadań.
  • To zmniejsza liczbę zależności zewnętrznych.
  • Warto dobrze znać bibliotekę standardową przed dodawaniem pakietów zewnętrznych.
200. Co wiesz o pakiecie `collections` i jakie inne moduły wbudowane były używane?

Python

collections udostępnia wysokopoziomowe struktury:

  • Counter, defaultdict, deque, namedtuple, ChainMap.

Typowo łączy się go z:

  • itertools do potoków iteracyjnych;
  • functools do buforowania wyników i narzędzi funkcyjnych;
  • pathlib, json, datetime w zadaniach praktycznych.

W skrócie:

  • collections rozszerza podstawowe kontenery o praktyczne struktury.
  • Często zwiększa prostotę i wydajność kodu.
  • Dobrze współpracuje z itertools i functools.
201. Co zwraca `sys.argv`?

Python

sys.argv zwraca listę argumentów wiersza poleceń:

  • sys.argv[0] to nazwa skryptu;
  • pozostałe elementy to przekazane argumenty.

W skrócie:

  • sys.argv to podstawowy interfejs argumentów CLI.
  • Wartości przychodzą jako napisy.
  • Dla bardziej złożonych CLI lepsze jest argparse.
202. Jaki jest główny moduł do pracy z systemem operacyjnym w Pythonie?

Python

Podstawowym modułem jest os, razem z os.path, a dla nowoczesnego API ścieżek zalecany jest pathlib.

W skrócie:

  • os daje dostęp do API procesów, środowiska i systemu plików.
  • pathlib jest wygodniejszy do pracy ze ścieżkami.
  • W realnych projektach często używa się obu.
203. Jak przetasować elementy listy za pomocą modułu `random`?

Python

Użyj random.shuffle(list_) do tasowania in-place.

import random
items = [1, 2, 3, 4]
random.shuffle(items)

W skrócie:

  • shuffle zmienia oryginalną listę.
  • Działa tylko z mutowalnymi sekwencjami, na przykład listami.
  • Aby otrzymać nową kopię, użyj random.sample(items, k=len(items)).
204. Czym jest środowisko wirtualne?

Python

Środowisko wirtualne to izolowane środowisko Pythona z własnymi pakietami i wersjami zależności dla konkretnego projektu.

W skrócie:

  • Izolacja usuwa konflikty zależności między projektami.
  • Standardowym narzędziem jest venv.
  • To podstawowa praktyka dla odtwarzalnego rozwoju projektu.
205. Jak działa `pip`?

Python

pip instaluje, aktualizuje i usuwa pakiety z indeksów, najczęściej z PyPI, rozwiązuje zależności i instaluje je w bieżącym środowisku.

W skrócie:

  • pip to standardowy menedżer pakietów Pythona.
  • Działa w obrębie aktywnego venv i interpretera.
  • Dla stabilności ważne są przypięte wersje.
206. Czym jest `requirements.txt`?

Python

requirements.txt to plik z listą zależności, często z dokładnymi wersjami, używany do odtwarzalnej instalacji.

W skrócie:

  • Format jest prosty i zgodny z pip install -r.
  • W środowisku produkcyjnym najlepiej przypinać dokładne wersje.
  • Często jest generowany narzędziami, na przykład pip-tools albo poetry export, na podstawie deklaratywnej listy zależności lub plików lock.
207. Czym jest `pyproject.toml` i dlaczego stał się standardem?

Python

pyproject.toml to ustandaryzowany plik konfiguracji projektu: metadane pakietu, system budowania i ustawienia narzędzi, takich jak ruff, pytest, mypy.

W skrócie:

  • Centralizuje konfigurację projektu Python.
  • Jest wspierany przez nowoczesne narzędzia pakowania.
  • Zmniejsza liczbę rozproszonych plików konfiguracyjnych.
208. Jak zarządzać zależnościami w nowoczesnym projekcie Pythona?

Python

  • izolować środowisko przez venv;
  • definiować zależności w pyproject.toml;
  • przypinać pliki lock albo constraints dla odtwarzalności;
  • regularnie aktualizować z kontrolą przez CI.

W skrócie:

  • Zarządzanie zależnościami powinno być odtwarzalne.
  • Nie mieszaj pakietów globalnych i projektowych.
  • Aktualizacje wykonuj w kontrolowany sposób, przez testy.
209. Jak poprawnie zorganizować strukturę dużego projektu Pythona?

Python

  • podzielić kod na pakiety domenowe;
  • mieć osobne warstwy, takie jak api, services, domain, infrastructure;
  • wydzielić tests/, scripts/, configs/;
  • utrzymywać jawne granice modułów i publiczne API.

W skrócie:

  • Struktura powinna odzwierciedlać domenę, a nie przypadkowe szczegóły techniczne.
  • Wyraźne granice zmniejszają liczbę cyklicznych zależności.
  • Testy i narzędzia powinny być pełnoprawną częścią drzewa projektu.
210. Jaka jest różnica między testowaniem automatycznym a ręcznym i jakie są zalety testowania automatycznego?

Python

Testowanie ręczne wykonuje człowiek krok po kroku. Testowanie automatyczne jest wykonywane przez skrypty albo struktury testowe.

W skrócie:

  • Testy automatyczne są szybkie, powtarzalne i nadają się do CI.
  • Testowanie ręczne jest przydatne dla eksploracyjnych scenariuszy UX.
  • W praktyce produkcyjnej potrzebne jest połączenie obu podejść.
211. Czym jest TDD (Test-Driven Development)?

Python

TDD to cykl: napisać nieprzechodzący test -> minimalna implementacja -> refaktoryzacja.

W skrócie:

  • TDD kształtuje API przez testy.
  • Daje szybki sygnał zwrotny o regresjach.
  • Najlepiej sprawdza się w modułowej logice biznesowej.
212. Jakie są popularne narzędzia testowe w Pythonie?

Python

Najpopularniejsze to pytest, unittest z biblioteki standardowej oraz hypothesis do testów opartych na właściwościach.

W skrócie:

  • pytest jest najczęściej wybierany do nowych projektów.
  • unittest jest przydatny jako standardowe podstawowe narzędzie.
  • hypothesis wzmacnia pokrycie przypadków brzegowych.
213. Czym jest `unittest` w Pythonie?

Python

unittest to wbudowane narzędzie w stylu xUnit do testów: klasy przypadków testowych, metody asercji, przygotowanie i sprzątanie oraz uruchamianie testów.

W skrócie:

  • Jest częścią biblioteki standardowej.
  • Nadaje się do konserwatywnych środowisk bez zależności zewnętrznych.
  • Ma bardziej rozbudowaną składnię niż pytest.
214. Czym jest `pytest` i czym różni się od `unittest` pod względem składni i funkcjonalności?

Python

pytest to narzędzie z prostą składnią testów funkcyjnych, rozbudowanymi fixture'ami, parametryzacją i ekosystemem wtyczek.

W skrócie:

  • pytest ma mniej szablonowy styl i jest wygodniejszy dla dużych zestawów testów.
  • unittest jest oparty na klasach i standardowy w bibliotece standardowej.
  • W nowoczesnych projektach dominuje pytest.
215. Opisz, jak metoda `pytest.raises` jest używana do testowania wystąpienia konkretnych wyjątków w kodzie Pythona.

Python

pytest.raises(ExpectedError) sprawdza, czy blok kodu zgłasza dokładnie oczekiwany wyjątek.

import pytest
with pytest.raises(ValueError):
    int("abc")

W skrócie:

  • Jawnie testuje scenariusze negatywne.
  • Chroni przed "cichym" przechodzeniem błędów.
  • Może też sprawdzać komunikat lub atrybuty wyjątku.
216. Czym jest parametryzacja w testach i jak pytest wspiera ją przez `@pytest.mark.parametrize`?

Python

Parametryzacja uruchamia jeden test dla kilku zestawów danych wejściowych. W pytest robi się to dekoratorem @pytest.mark.parametrize.

W skrócie:

  • Daje mniej duplikacji kodu testowego.
  • Łatwo skalować przypadki.
  • Zwiększa przejrzystość pokrycia scenariuszy wejściowych.
217. Jak w pytest nadać własne nazwy testom w parametryzacji dla lepszej czytelności?

Python

Użyj parametru ids w parametrize.

@pytest.mark.parametrize("value,expected", [(2, True), (3, False)], ids=["even", "odd"])

W skrócie:

  • ids czyni wynik uruchomienia testów bardziej zrozumiałym.
  • Ułatwia diagnozowanie niepowodzeń.
  • Jest szczególnie przydatny przy dużej liczbie przypadków.
218. Czym jest etap przygotowania testu i dlaczego to ważne?

Python

Etap przygotowania testu to ustawienie stanu testowego: danych, obiektów, mocków i środowiska. Dobrze przygotowane środowisko sprawia, że test jest deterministyczny.

W skrócie:

  • Niestabilne przygotowanie oznacza niestabilne testy.
  • Dobre przygotowanie izoluje test od efektów zewnętrznych.
  • Jasny etap przygotowania poprawia czytelność scenariusza.
219. Jakie etapy obejmuje sprzątanie po teście i dlaczego to ważne?

Python

Sprzątanie po teście usuwa wszystko, co utworzył test: pliki tymczasowe, połączenia, mocki i testowe rekordy w bazie danych.

W skrócie:

  • Sprzątanie zapewnia izolację między testami.
  • Zmniejsza efekty uboczne i niestabilność testów.
  • W pytest wygodnie robi się to przez finalizery fixture i fixture oparte na yield.
220. Jaka jest różnica między metodami `setUp()` i `setUpClass()` w unittest?

Python

setUp() wykonuje się przed każdą metodą testową. setUpClass() jako metoda klasowa wykonuje się jeden raz przed wszystkimi testami klasy.

W skrócie:

  • setUp służy do izolacji na poziomie pojedynczego testu.
  • setUpClass służy do kosztownych współdzielonych zasobów.
  • Nadmierny współdzielony stan przez setUpClass może komplikować testy.
221. Czym jest obiekt pozorowany i jak pomaga podnieść jakość testów?

Python

Obiekt pozorowany to obiekt zastępczy, który imituje zależność i pozwala kontrolować zachowanie oraz sprawdzać wywołania.

W skrócie:

  • Mocki izolują testowaną jednostkę od usług zewnętrznych.
  • Sprawiają, że testy są szybsze i stabilniejsze.
  • Pozwalają sprawdzać interakcje, a nie tylko wynik.
222. Jaka jest różnica między użyciem `mock.patch` w unittest a `monkeypatch` w pytest do mockowania obiektów?

Python

mock.patch z unittest.mock podmienia obiekty według ścieżki importu i ma rozbudowane API do sprawdzania wywołań. monkeypatch w pytest w prostszy sposób zmienia atrybuty, zmienne środowiskowe i słowniki na czas testu.

W skrócie:

  • patch jest potężniejszy w scenariuszach z asercjami na mockach.
  • monkeypatch jest wygodny do szybkich testowych podmian.
  • Często używa się ich łącznie, zależnie od przypadku.
223. Jakie jest przeznaczenie parametru `scope` w fixture'ach pytest?

Python

scope określa cykl życia fixture'a: function, class, module, package, session.

W skrócie:

  • Mniejszy scope oznacza lepszą izolację.
  • Większy scope oznacza szybsze uruchamianie dużych zestawów testów.
  • Wybór zasięgu to balans między szybkością a niezależnością.
224. Czym jest złożoność algorytmu i jak się ją określa?

Python

Złożoność algorytmu ocenia, jak rosną koszty czasu i pamięci wraz ze wzrostem rozmiaru danych wejściowych n.

W skrócie:

  • Mierzy się złożoność czasową i pamięciową.
  • Ocena jest zwykle asymptotyczna.
  • Pomaga wybierać algorytm i struktury danych.
225. Wyjaśnij pojęcie notacji Big O i jej znaczenie w ocenie złożoności algorytmów.

Python

Big O opisuje górną granicę wzrostu kosztu algorytmu przy dużym n, pomijając stałe i składniki niższego rzędu.

W skrócie:

  • Big O pokazuje skalowalność, a nie dokładny czas.
  • Daje wspólny język do porównywania algorytmów.
  • Jest krytycznie ważna dla wydajności przy dużych wolumenach danych.
226. Jakie rodzaje złożoności algorytmicznej występują najczęściej?

Python

Najczęstsze to O(1), O(log n), O(n), O(n log n), O(n^2).

W skrócie:

  • O(n) i O(n log n) są najtypowsze w zadaniach praktycznych.
  • O(n^2) i więcej często staje się wąskim gardłem.
  • Trzeba brać pod uwagę także pamięć, a nie tylko czas.
227. Podaj przykłady zadań, które są efektywnie rozwiązywane przez algorytmy liniowe `O(n)`.

Python

Przykłady:

  • szukanie maksimum i minimum;
  • filtrowanie elementów;
  • zliczanie częstotliwości przez Counter;
  • sprawdzanie warunków dla każdego elementu.

W skrócie:

  • O(n) oznacza jedno przejście po danych.
  • To rozwiązanie optymalne dla wielu agregacji.
  • Algorytmy liniowe dobrze się skalują.
228. Jak działa `lru_cache` i kiedy go używać?

Python

functools.lru_cache buforuje wyniki funkcji według argumentów i zwraca gotową wartość przy kolejnych wywołaniach.

W skrócie:

  • Jest skuteczny dla funkcji czystych z powtarzającymi się danymi wejściowymi.
  • Nie nadaje się do funkcji z efektami ubocznymi.
  • maxsize kontroluje rozmiar bufora w pamięci.
229. Jak profilować wydajność kodu Pythona?

Python

Podstawowe narzędzia:

  • timeit do mikrobenchmarków;
  • cProfile i pstats do profilowania na poziomie wywołań;
  • py-spy i scalene do analizy zbliżonej do warunków produkcyjnych.

W skrócie:

  • Optymalizuj dopiero po pomiarach.
  • Profiluj realistyczne scenariusze obciążenia.
  • Ustalaj punkt odniesienia przed i po zmianach.
230. Jakie są podstawowe sposoby optymalizacji kodu Pythona?

Python

  • dobrać właściwe struktury danych;
  • zmniejszyć złożoność asymptotyczną;
  • unikać zbędnych kopii;
  • używać generatorów i leniwych potoków;
  • buforować kosztowne obliczenia;
  • przenosić najbardziej gorące ścieżki do C, Rust lub NumPy, jeśli to potrzebne.

W skrócie:

  • Największy zysk daje algorytm, a nie składniowa sztuczka.
  • Optymalizacja musi opierać się na profilowaniu.
  • Nie warto poświęcać czytelności bez zmierzonej korzyści.
231. Kiedy warto używać rozszerzeń C albo PyPy?

Python

Rozszerzenia C są zasadne dla wąskich gardeł CPU i integracji z bibliotekami natywnymi. PyPy jest zasadne wtedy, gdy długowieczny czysty kod Pythona korzysta z kompilacji JIT.

W skrócie:

  • Rozszerzenie C daje maksymalną wydajność kosztem złożoności budowania.
  • PyPy daje potencjalny wzrost wydajności bez przepisywania kodu na C.
  • Wybór powinien wynikać z pomiarów wydajności dla rzeczywistego obciążenia.
232. Czym jest profilowanie pamięci?

Python

Profilowanie pamięci to mierzenie, gdzie i ile pamięci zużywa kod w czasie, aby znaleźć wycieki i kosztowne miejsca.

W skrócie:

  • Pokazuje gorące miejsca pamięci i piki zużycia.
  • Jest przydatne dla obciążeń wsadowych i przetwarzania danych.
  • Narzędzia: tracemalloc, memory_profiler, scalene.
233. Czym jest GIL (globalna blokada interpretera)?

Python

GIL to mechanizm w CPython, który pozwala tylko jednemu wątkowi jednocześnie wykonywać kod bajtowy Pythona w obrębie procesu.

W skrócie:

  • GIL wpływa na wielowątkowość w zadaniach CPU-bound.
  • W zadaniach I/O-bound wątki nadal są użyteczne.
  • Do równoległości CPU częściej używa się multiprocessing.
234. Jak globalna blokada interpretera Pythona wpływa na współbieżność w CPython i jakie ma to konsekwencje dla wielowątkowości?

Python

Przez GIL wątki w CPython nie wykonują kodu bajtowego Pythona naprawdę równolegle dla kodu CPU-bound. Przełączają się współbieżnie.

Konsekwencje:

  • dla wątków I/O-bound efekt jest dobry, bo oczekiwanie na I/O może się nakładać;
  • dla zadań CPU-bound zysk z wątków jest zwykle ograniczony.

W skrócie:

  • GIL ogranicza równoległość wątków w scenariuszach CPU-bound.
  • Wątki pozostają użyteczne dla sieci i dysku.
  • Dla CPU lepiej używać procesów albo obliczeń natywnych.
235. Jak GIL wpływa na wydajność?

Python

GIL prawie nie przeszkadza w zadaniach I/O-bound, ale ogranicza przepustowość wielowątkowego kodu Pythona CPU-bound w jednym procesie.

W skrócie:

  • Wpływ GIL zależy od rodzaju obciążenia.
  • CPU-bound plus wątki w CPython często się nie skaluje.
  • Architekturę należy dobierać do profilu zadań.
236. Wyjaśnij koncepcję wielowątkowości w Pythonie i czym różni się od multiprocessing.

Python

threading uruchamia kilka wątków w jednym procesie ze współdzieloną pamięcią. multiprocessing uruchamia osobne procesy z odrębną pamięcią.

W skrócie:

  • Wątki są lżejsze i wygodne dla zadań I/O-bound.
  • Procesy dają rzeczywistą równoległość CPU.
  • Procesy mają większy narzut IPC i uruchamiania.
237. Kiedy używać multiprocessing zamiast threading?

Python

Gdy zadanie jest CPU-bound i wymaga użycia wielu rdzeni w CPython. Przykłady: ciężkie parsowanie, przetwarzanie obrazu i wideo, obliczenia numeryczne.

W skrócie:

  • CPU-bound zwykle oznacza multiprocessing.
  • I/O-bound zwykle oznacza threading albo asyncio.
  • Trzeba uwzględniać koszt serializacji między procesami.
238. Jaka jest różnica między współbieżnością a równoległością w programowaniu i kiedy której z nich używać?

Python

Współbieżność oznacza nakładanie się zadań w czasie. Równoległość oznacza fizyczne wykonywanie zadań jednocześnie na kilku rdzeniach.

W skrócie:

  • Współbieżność jest przydatna przy opóźnieniach I/O.
  • Równoległość jest potrzebna dla obliczeń intensywnie obciążających CPU.
  • W Pythonie wybór narzędzia zależy od rodzaju wąskiego gardła.
239. Czym jest współbieżność w Pythonie?

Python

Współbieżność w Pythonie to organizacja wykonywania wielu zadań tak, aby postępowały współbieżnie: przez wątki, asyncio albo procesy.

W skrócie:

  • Chodzi o zarządzanie wieloma zadaniami, niekoniecznie równolegle.
  • Pozwala zwiększyć przepustowość scenariuszy I/O.
  • Wymaga starannego projektowania synchronizacji i anulowania.
240. Jaka jest różnica między zadaniami I/O-bound a CPU-bound?

Python

Zadania I/O-bound głównie czekają na sieć, dysk albo bazę danych. Zadania CPU-bound spędzają większość czasu na obliczeniach procesora.

W skrócie:

  • I/O-bound dobrze skaluje się przez asyncio i wątki.
  • CPU-bound lepiej skaluje się przez multiprocessing i kod natywny.
  • Najpierw określ wąskie gardło przez profilowanie.
241. Do czego służy moduł `asyncio` w Pythonie i jak pozwala realizować programowanie asynchroniczne?

Python

asyncio udostępnia pętlę zdarzeń, planowanie zadań i asynchroniczne prymitywy do współbieżności kooperatywnej w zadaniach I/O-bound.

W skrócie:

  • Pozwala efektywnie obsługiwać wiele operacji I/O.
  • Opiera się na async i await.
  • Dobrze nadaje się do usług i klientów sieciowych.
242. Czym różni się programowanie synchroniczne od asynchronicznego w Pythonie?

Python

Programowanie synchroniczne oznacza, że wywołanie blokuje bieżący wątek do zakończenia. Programowanie asynchroniczne oznacza, że await oddaje sterowanie pętli zdarzeń, gdy operacja czeka na I/O.

W skrócie:

  • Async zmniejsza czas bezczynności podczas I/O.
  • Sync jest prostszy dla logiki liniowej.
  • Async dodaje złożoność zarządzania zadaniami i anulowaniem.
243. Czym są `async` i `await`?

Python

async def definiuje funkcję typu coroutine. await wstrzymuje coroutine do gotowości obiektu awaitable i oddaje sterowanie pętli.

W skrócie:

  • To składnia asynchronicznego modelu kooperatywnego.
  • Używa się jej razem z asyncio i bibliotekami asynchronicznymi.
  • await można stosować tylko wewnątrz async def.
244. Jak działa `asyncio`?

Python

asyncio uruchamia pętlę zdarzeń, która wykonuje zadania, czyli coroutines, przełączając się w punktach await i planując gotowe operacje I/O.

Krytyczna zasada: Pętla zdarzeń działa w jednym wątku. Każda blokująca operacja, taka jak time.sleep(), synchroniczne żądania requests albo ciężkie obliczenia, zatrzymuje całą pętlę i wszystkie inne zadania.

W skrócie:

  • Jeden wątek może obsługiwać wiele zadań I/O dzięki kooperatywności.
  • Planowanie nie wywłaszcza zadań, czyli jest niewywłaszczające.
  • Blokujące wywołania niszczą wydajność asyncio.
245. Czym jest pętla zdarzeń?

Python

Pętla zdarzeń to planista, który śledzi zdarzenia oraz gotowość I/O i uruchamia odpowiednie funkcje zwrotne oraz coroutines.

W skrócie:

  • To centralny komponent modelu asyncio.
  • Zarządza cyklem życia zadań asynchronicznych.
  • Określa, kiedy dana coroutine ma wznowić wykonanie.
246. Jak `asyncio` pozwala realizować programowanie asynchroniczne i jakie główne komponenty biorą udział w kodzie `asyncio`?

Python

Kluczowe komponenty:

  • pętla zdarzeń;
  • coroutine, czyli async def;
  • task, czyli asyncio.create_task;
  • awaitables, czyli futures, tasks i coroutines;
  • prymitywy synchronizacji, takie jak Lock, Queue, Semaphore.

W skrócie:

  • asyncio łączy planowanie i asynchroniczne API w jeden model.
  • Zadania kooperatywnie współdzielą jeden wątek wykonania.
  • Architektura powinna uwzględniać timeouty, ponawianie i anulowanie.
247. Kiedy `asyncio` nie daje korzyści?

Python

Gdy obciążenie jest CPU-bound albo główne biblioteki są blokujące i nie mają API asynchronicznego. Nie opłaca się też w prostych krótkich skryptach.

Rozwiązanie dla blokującego kodu: Jeśli trzeba użyć biblioteki blokującej w środowisku asynchronicznym, można użyć loop.run_in_executor(None, sync_func), aby uruchomić ją w osobnym wątku i nie blokować pętli zdarzeń.

W skrócie:

  • Async nie przyspiesza czystych obliczeń.
  • Bez nieblokujących bibliotek I/O korzyść jest minimalna.
  • run_in_executor pomaga integrować starszy kod synchroniczny.
  • Złożoność podejścia asynchronicznego powinna być uzasadniona obciążeniem.
248. Jak działa anulowanie w `asyncio`?

Python

Anulowanie zadania przez task.cancel() podnosi CancelledError w coroutine. Kod powinien poprawnie obsługiwać czyszczenie w try/finally.

W skrócie:

  • Anulowanie jest zwykłym przepływem sterowania w async.
  • Obsługę anulowania trzeba uwzględniać w projekcie coroutine.
  • Ignorowanie anulowania prowadzi do "zawieszonych" zadań.
249. Czym jest `contextvars`?

Python

contextvars daje zmienne lokalne dla kontekstu, bezpieczne w scenariuszach asynchronicznych i wielowątkowych. Przydaje się dla request-id, correlation-id i kontekstu tenantów.

W skrócie:

  • To alternatywa dla globalnego stanu w kodzie współbieżnym.
  • Wartość jest izolowana w ramach kontekstu.
  • Poprawia śledzenie i obserwowalność.
250. Jakie najlepsze praktyki warto stosować przy pisaniu kodu w Pythonie?

Python

  • przestrzegać PEP 8 i automatyzować formatowanie;
  • pisać jawne adnotacje typów dla publicznego API;
  • używać with do pracy z zasobami;
  • pokrywać logikę biznesową testami;
  • unikać przedwczesnej optymalizacji i profilować;
  • trzymać moduły niewielkie i o jasnej odpowiedzialności;
  • zarządzać zależnościami przez pyproject.toml i strategię lock.

W skrócie:

  • Czytelność i przewidywalność są ważniejsze niż "sztuczki".
  • Jakość opiera się na automatyzacji: lint, typy, testy i CI.
  • Prostota architektury zmniejsza koszt utrzymania.