Read in other languages: English 🇺🇸, Polski 🇵🇱, German 🇩🇪, French 🇫🇷, Spanish 🇪🇸, Ukraiński 🇺🇦.
1. Czym jest Python i jakie są jego główne cechy?
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?
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 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?
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" # strTypy 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?
Strong typing w Pythonie oznacza, że interpreter nie wykonuje niebezpiecznych, niejawnych konwersji między niezgodnymi typami.
1 + "2" # TypeErrorDla operacji na różnych typach potrzebne jest jawne rzutowanie:
1 + int("2") # 3W 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?
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.
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 literalW 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?
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?
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?
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:
passjest 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?
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?
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+)?
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](...) -> ...); tomllibw 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/casei nowoczesnytypingmają najbardziej zauważalny wpływ na kod.- Nowych funkcji warto domyślnie używać w nowym kodzie.
14. Czym jest docstring w Pythonie?
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?
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ęć?
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?
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
ruffdo lintu +blackdo formatowania.
W skrócie:
blackkoncentruje się na formatowaniu.ruffpokrywa 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.
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?
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.
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:
printnadaje 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`?
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 * 2Kluczowe komendy: n (next), s (step), c (continue), p expr
(print expr), l (list), bt (backtrace).
W skrócie:
pdbdaje interaktywną kontrolę nad wykonaniem.- Jest skuteczniejszy niż
printw 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?
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?
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`?
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:
loggingnadaje się do produkcji,printnie.- Poziomy logów dają kontrolę nad szumem.
- Logi powinny być ustrukturyzowane i spójne.
25. Czym jest traceback?
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?
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?
/ 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 # -4W 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?
Operator % zwraca resztę z dzielenia.
Sprawdzenie parzystości:
def is_even(n: int) -> bool:
return n % 2 == 0Jeś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 % 2to 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?
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?
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 / bW 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?
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:
randomnadaje 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?
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:
floatjest 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 `\'`.
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ą?
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
stripnie 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?
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) # 2Zwraca 0, jeśli nie ma żadnych wystąpień.
W skrócie:
countszybko 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?
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:
jointo 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.
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?
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:
replacenie zmienia stringa in-place.- Bez
countzamienia wszystkie wystąpienia. countpozwala kontrolować liczbę zamian.
39. Jak działa metoda `split` w stringach?
split(sep=None, maxsplit=-1) dzieli string na listę podciągów.
- bez
sepdzieli po dowolnych białych znakach; - z
sepdzieli po konkretnym separatorze; maxsplitogranicza 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:
splitzamienia string na listę tokenów.sep=Nonema specjalne zachowanie dla whitespace.maxsplitjest przydatny przy parsowaniu formatów key/value.
40. Jak działa konwersja typów (type casting)?
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?
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") # TrueW skrócie:
- Konwersja boolean opiera się na regułach truthy/falsy.
- Puste i zerowe ->
False. - Niepusty string
"0"nadal jestTrue.
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ę?
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 NoneW skrócie:
int()ifloat()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 pozwala na porównania liczbowe zgodnych typów numeric.
intifloatsą porównywane według wartości.booljest podklasąint:False == 0,True == 1.
1 == 1.0 # True
True == 1 # True
False < 1 # TrueDla niezgodnych typów, na przykład int i str, porównanie porządku (<,
>) powoduje TypeError.
W skrócie:
int,floatiboolmają wspólną semantykę numeric.boolzachowuje się jak0/1w porównaniach.- Niezgodne typy nie są porównywane operatorami porządku.
44. Jak Python obsługuje porównania między listami a set?
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} # TypeErrorJeśli trzeba porównać skład elementów, najpierw ujednolić typ:
set([1, 2]) == {1, 2} # TrueW skrócie:
listisetsą porównywane jako różne struktury danych.==między nimi zwykle dajeFalse.- Aby porównać sensownie zawartość, najpierw sprowadź do jednego typu.
45. Opisz użycie funkcji `bool()` w Pythonie.
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) # FalseW 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`?
Główna różnica: list jest mutable, a tuple immutable.
Praktyczne konsekwencje:
listnadaje się do danych, które się zmieniają;tuplenadaje się do stałych rekordów;tuplemoż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:
listsłuży do zmiennych kolekcji.tuplesł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`?
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:
tupletworzy się przez literał albotuple(iterable).single = (x,)to poprawny jednoelementowy tuple.- Pusty tuple:
().
48. Jaka jest różnica między metodą `reverse` a slicingiem `[::-1]` przy odwracaniu listy?
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 listaW 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?
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`?
Przypisanie kopiuje tylko referencję:
a = [1, 2]
b = a
b.append(3) # a też się zmienia.copy() tworzy nową, płytką listę:
a = [1, 2]
b = a.copy()
b.append(3) # a się nie zmieniDla 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?
pop() usuwa i zwraca element z listy.
pop()bez argumentów usuwa ostatni element;pop(i)usuwa element o indeksiei.
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?
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?
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]) # TrueW 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?
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:
dictjest 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`?
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) # 3W skrócie:
[]służy do obowiązkowych kluczy.get()nadaje się do opcjonalnych pól.get()zmniejsza liczbętry/exceptprzy odczycie danych.
56. Opisz, jak można aktualizować i usuwać elementy ze słownika.
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ę
[]iupdate(). - 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?
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/itemssą 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ą?
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:
dictto wysokowydajna struktura hashująca.- Szybkość osiąga dzięki haszowaniu.
- Klucze muszą być hashable i stabilne.
59. Czym jest hash function?
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, tohash(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?
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:
setjest 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?
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,setnie 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()`?
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?
Popularne typy immutable:
int,float,bool,complex;str,bytes;tuplejeś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
dicti elementyset.
64. Jakie typy danych są mutable?
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?
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`?
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:
Noneoznacza 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?
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:
idpomaga 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 `==`?
== 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 # FalseWaż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.isdotyczy dokładnie tego samego obiektu w pamięci.- Interning może dawać nieoczywiste
is Truedla małychintistr. value is Noneto jedyny poprawny styl sprawdzaniaNone.
69. Jak stosuje się Singleton pattern w Pythonie? Podaj przykłady obiektów typu Singleton w Pythonie.
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;TrueiFalse;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?
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:
continueW skrócie:
breakzatrzymuje całą pętlę.continuepomija 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?
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/finallydo czyszczenia zasobów.
while True:
task = queue.get()
if task is None:
break
handle(task)W skrócie:
while Truejest 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ć?
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
setlubdict; - wynosić niezmienniki poza pętlę wewnętrzną;
- używać generatorów,
itertoolsi 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`)?
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/casejest 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`?
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?
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?
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/**kwargssą przydatne w rozszerzalnych interfejsach.
77. Czym są argumenty pozycyjne i nazwane?
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) # nazwaneW 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ć?
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 bucketW 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ą?
*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.
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?
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:
lambdajest 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 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?
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?
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 incW skrócie:
- Zmienna lokalna należy do bieżącej funkcji.
nonlocalzmienia stan funkcji zewnętrznej.- To kluczowy mechanizm dla closure ze stanem.
85. Czym jest unpacking w Pythonie i jak stosuje się go przy przypisaniu?
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.
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?
* 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?
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?
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 wrapperZastosowania: 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?
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.
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?
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?
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 decoratorW 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?
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:
wrapszachowuje "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?
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,setidict. - 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?
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?
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
forw jednym wyrażeniu. - Kolejność
forodpowiada 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?
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?
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łaszaStopIteration.
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()`?
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?
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) # NoneW skrócie:
next()daje ręczną kontrolę nad krokiem iteracji.- Bez
defaultkoniec strumienia oznaczaStopIteration. - Z
defaultmożna wygodnie i bezpiecznie czytać strumień.
102. Czy można zamiennie używać metod `__next__()` i `__iter__()` z funkcjami `next()` oraz `iter()`?
Tak, ale z uwzględnieniem protokołu:
iter(obj)wywołujeobj.__iter__();next(it)wywołujeit.__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?
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:
StopIterationto 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?
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?
Trzeba zdefiniować funkcję z yield.
def countdown(start: int):
current = start
while current > 0:
yield current
current -= 1Wywołanie countdown(3) zwraca obiekt generatora, po którym można iterować.
W skrócie:
- Obecność
yieldsprawia, ż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ęć?
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:
yieldrealizuje 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`?
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:
returnoznacza zakończenie funkcji.yieldoznacza etapowe zwracanie wartości.yieldjest używane przy przetwarzaniu strumieniowym.
108. Czym jest Object-Oriented Programming (OOP) i jakie są jego główne zasady w Pythonie?
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?
class to szablon do tworzenia obiektów z atrybutami i metodami.
class User:
def __init__(self, name: str) -> None:
self.name = nameKlasa 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?
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?
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-metodaW 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?
__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 = balanceW 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?
self to referencja do bieżącej instancji. Przez self metoda odczytuje i zmienia
atrybuty instancji.
def deposit(self, amount: float) -> None:
self.balance += amountNazwa self nie jest zarezerwowana składniowo, ale jest powszechnie przyjętym standardem.
W skrócie:
selfwiąże metodę z konkretnym obiektem.- Przez
selfdostępne są dane instancji. - To obowiązkowy pierwszy parametr metody instancji.
114. Jak definiować metody w klasie?
Metody definiuje się jako funkcje wewnątrz class.
class Calculator:
def add(self, a: int, b: int) -> int:
return a + bTypowe 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.
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.
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.
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ą?
- Metoda instancji ma
selfi działa na konkretnej instancji. - Metoda klasy ma
clsi 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?
@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:
classmethoddział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ć?
@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:
staticmethodto 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?
Różnica dotyczy pierwszego argumentu i poziomu dostępu:
@classmethodotrzymujeclsi może pracować ze stanem klasy;@staticmethodnie 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?
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 instancjiW 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?
__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 = yNiuanse 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"?
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__`?
__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
withlubtry/finally.
126. Podaj przykład użycia magicznej metody `__str__` do zdefiniowania tekstowej reprezentacji własnej klasy w Pythonie.
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__`?
__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?
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?
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?
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?
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?
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?
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 = valueW 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.
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)) # TrueW skrócie:
isinstancejest 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.
issubclass(Sub, Base) sprawdza, czy klasa Sub jest podklasą Base.
class Animal: ...
class Dog(Animal): ...
issubclass(Dog, Animal) # TrueW 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?
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?
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?
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?
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?
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?
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#.
_namei__namesą sygnałami intencji dla programistów.- Realna kontrola dostępu wynika z projektu API.
142. Czym jest polimorfizm i jak jest realizowany w Pythonie?
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?
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?
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 = valueW skrócie:
- Abstrakcja danych chroni inwarianty obiektu.
propertydaje kontrolowany dostęp do stanu.- Szczegóły wewnętrzne można zmieniać bez zmiany API.
145. Czym są ABC i `@abstractmethod`?
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.
@abstractmethoduniemoż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?
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._nameW skrócie:
propertydaje 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`?
@property to dekorator dla metody getter. Razem z @x.setter i @x.deleter
tworzy kontrolowany atrybut.
W skrócie:
@propertypozwala 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?
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?
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:
propertyjest prostsze i lokalne.- Deskryptor jest bardziej elastyczny i skalowalny.
propertyjest faktycznie zbudowane na protokole deskryptora.
150. Kiedy lepiej używać property, a kiedy deskryptora?
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?
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/hasattrsł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.
__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 = nameW 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ć?
@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 = TrueW skrócie:
dataclassskraca 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?
dataclass koncentruje się na wygodnym opisie struktury. Pydantic dodaje
walidację w czasie działania, parsowanie i serializację danych.
W skrócie:
dataclassjest 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?
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`?
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:
mypywykonuje 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?
- konsekwentnie dodawać adnotacje typów do publicznego API;
- włączyć
mypyalbopyrightw CI; - używać
TypedDict,Protocoli generyków; - minimalizować
Anyi 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`?
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: boolW skrócie:
TypedDicttypujedicto stałych kluczach.- Jest wygodny dla struktur podobnych do JSON.
- Jest sprawdzany przez analizator statyczny.
159. Czym jest `Protocol` w typowaniu?
Protocol opisuje kontrakt behawioralny, czyli typowanie strukturalne: typ jest zgodny,
jeśli ma wymagane metody i atrybuty, niezależnie od dziedziczenia.
W skrócie:
Protocolrealizuje 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?
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?
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?
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ą?
- 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`?
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:
elsewarto zostawić dla kodu "ścieżki sukcesu".finallysłuży do zasobów i czyszczenia.- Łap konkretne wyjątki, a nie "wszystko po kolei".
165. Co oznacza kolejność bloków `except`?
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ść
exceptwpływa na to, który obsługujący blok zostanie uruchomiony. - Szeroki
exceptna 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?
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:
assertdokumentuje, ż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?
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:
raisejest mechanizmem obsługi błędów, aprintnie.- Wyjątki można centralnie przechwytywać i logować.
printsłuży do diagnostyki, a nie do kontraktu błędów.
168. Jak utworzyć własny wyjątek?
Utwórz klasę dziedziczącą po Exception albo po bardziej konkretnym wyjątku bazowym.
class InvalidOrderError(Exception):
passW 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?
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?
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.
BaseExceptionzwykle 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?
Podejścia:
- osobne
exceptdla 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?
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ść?
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?
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:
withgwarantuje 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?
Podstawowe tryby:
rodczyt;wnadpisanie;adopisywanie na końcu;xutworzenie nowego pliku;btryb binarny;ttryb tekstowy, domyślny;+odczyt i zapis.
W skrócie:
- Tryb określa semantykę bezpieczeństwa i zmian w pliku.
wusuwa poprzednią zawartość, aają 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?
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.
withautomatyzuje 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?
read()czyta cały plik albonbajtów lub znaków;readline()czyta jeden wiersz;readlines()czyta wszystkie wiersze do listy.
W skrócie:
read()ireadlines()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`?
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:
wusuwa stare dane.azachowuje istniejącą zawartość.- Do logów zwykle używa się
a.
179. Jak efektywnie pracować z dużymi plikami?
- 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?
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`?
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:
withgwarantuje 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?
Dwa podejścia:
- klasa z
__enter__i__exit__; - funkcja z dekoratorem
contextlib.contextmanager.
from contextlib import contextmanager
@contextmanager
def temp_flag():
yieldW skrócie:
- Klasa nadaje się do złożonego stanu.
contextmanagerjest wygodny dla krótkich scenariuszy.- Oba podejścia dają gwarantowane czyszczenie.
183. Jak Python zarządza pamięcią?
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ą?
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?
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?
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?
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ą.
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 obiektuW 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ść?
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?
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?
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
aspoprawia czytelność i pomaga unikać konfliktów. - Warto preferować jawne importy.
192. Jakie są typy importów?
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?
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?
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?
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.
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?
__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?
- 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
rufforazisort.
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?
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?
collections udostępnia wysokopoziomowe struktury:
Counter,defaultdict,deque,namedtuple,ChainMap.
Typowo łączy się go z:
itertoolsdo potoków iteracyjnych;functoolsdo buforowania wyników i narzędzi funkcyjnych;pathlib,json,datetimew zadaniach praktycznych.
W skrócie:
collectionsrozszerza podstawowe kontenery o praktyczne struktury.- Często zwiększa prostotę i wydajność kodu.
- Dobrze współpracuje z
itertoolsifunctools.
201. Co zwraca `sys.argv`?
sys.argv zwraca listę argumentów wiersza poleceń:
sys.argv[0]to nazwa skryptu;- pozostałe elementy to przekazane argumenty.
W skrócie:
sys.argvto 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?
Podstawowym modułem jest os, razem z os.path, a dla nowoczesnego API ścieżek
zalecany jest pathlib.
W skrócie:
osdaje dostęp do API procesów, środowiska i systemu plików.pathlibjest 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`?
Użyj random.shuffle(list_) do tasowania in-place.
import random
items = [1, 2, 3, 4]
random.shuffle(items)W skrócie:
shufflezmienia 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?
Ś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`?
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:
pipto 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`?
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-toolsalbopoetry export, na podstawie deklaratywnej listy zależności lub plików lock.
207. Czym jest `pyproject.toml` i dlaczego stał się standardem?
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?
- 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?
- 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?
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)?
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?
Najpopularniejsze to pytest, unittest z biblioteki standardowej oraz hypothesis
do testów opartych na właściwościach.
W skrócie:
pytestjest najczęściej wybierany do nowych projektów.unittestjest przydatny jako standardowe podstawowe narzędzie.hypothesiswzmacnia pokrycie przypadków brzegowych.
213. Czym jest `unittest` w Pythonie?
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?
pytest to narzędzie z prostą składnią testów funkcyjnych, rozbudowanymi
fixture'ami, parametryzacją i ekosystemem wtyczek.
W skrócie:
pytestma mniej szablonowy styl i jest wygodniejszy dla dużych zestawów testów.unittestjest 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.
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`?
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?
Użyj parametru ids w parametrize.
@pytest.mark.parametrize("value,expected", [(2, True), (3, False)], ids=["even", "odd"])W skrócie:
idsczyni 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?
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?
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?
setUp() wykonuje się przed każdą metodą testową. setUpClass()
jako metoda klasowa wykonuje się jeden raz przed wszystkimi testami klasy.
W skrócie:
setUpsłuży do izolacji na poziomie pojedynczego testu.setUpClasssłuży do kosztownych współdzielonych zasobów.- Nadmierny współdzielony stan przez
setUpClassmoże komplikować testy.
221. Czym jest obiekt pozorowany i jak pomaga podnieść jakość testów?
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?
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:
patchjest potężniejszy w scenariuszach z asercjami na mockach.monkeypatchjest 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?
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?
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.
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?
Najczęstsze to O(1), O(log n), O(n), O(n log n), O(n^2).
W skrócie:
O(n)iO(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)`.
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ć?
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.
maxsizekontroluje rozmiar bufora w pamięci.
229. Jak profilować wydajność kodu Pythona?
Podstawowe narzędzia:
timeitdo mikrobenchmarków;cProfileipstatsdo profilowania na poziomie wywołań;py-spyiscalenedo 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?
- 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?
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?
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)?
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?
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ść?
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.
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?
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
threadingalboasyncio. - 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ć?
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?
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?
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
asyncioi wątki. - CPU-bound lepiej skaluje się przez
multiprocessingi 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?
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
asynciawait. - Dobrze nadaje się do usług i klientów sieciowych.
242. Czym różni się programowanie synchroniczne od asynchronicznego w Pythonie?
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`?
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
asyncioi bibliotekami asynchronicznymi. awaitmożna stosować tylko wewnątrzasync def.
244. Jak działa `asyncio`?
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ń?
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`?
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?
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_executorpomaga integrować starszy kod synchroniczny.- Złożoność podejścia asynchronicznego powinna być uzasadniona obciążeniem.
248. Jak działa anulowanie w `asyncio`?
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`?
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?
- przestrzegać PEP 8 i automatyzować formatowanie;
- pisać jawne adnotacje typów dla publicznego API;
- używać
withdo 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.tomli 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.