Wyjaśnienie generatorów Pythona i wydajności: oszczędzaj pamięć dzięki prawdziwym przykładom

  1. Strona główna
  2. Blog
  3. Rozwój oprogramowania

Rozwój oprogramowania

Wyjaśnienie generatorów Pythona i wydajności: Oszczędzaj pamięć na prawdziwych przykładach

Generatory generują wartości pojedynczo, zamiast tworzyć całą listę w pamięci. Przyjrzyj się dokładnie, jak wydajność wstrzymuje funkcję, zobacz zmierzoną przez nas różnicę w pamięci i zbuduj mały potok danych.

9 min czytania
Wyjaśnienie generatorów Pythona i wydajności: oszczędzaj pamięć na prawdziwych przykładach

Najważniejsze

  • Funkcja zawierająca yield zwraca generator, a jej treść nie jest uruchamiana, dopóki nie poprosisz o podanie wartości.
  • yield wstrzymuje funkcję i utrzymuje jej stan lokalny do następnego żądania.
  • Generatory używają małej, stałej ilości pamięci, niezależnie od tego, ile wartości generują.
  • Generator można zużyć tylko raz, co jest najczęstszą pułapką.

Wyobraź sobie, że ładujesz plik dziennika o rozmiarze 10 GB na listę tylko po to, aby znaleźć błędy. Twojemu programowi może zabraknąć pamięci, zanim ją znajdzie. Generatory Pythona rozwiązują ten problem, tworząc wartości na żądanie, pojedynczo, dzięki czemu możesz pracować z ogromnymi, a nawet nieskończonymi danymi, prawie bez pamięci.

Ten przewodnik pokazuje krok po kroku, co naprawdę dzieje się, gdy generator działa. Każdy przykład uruchomiliśmy w Pythonie 3.13, a dane wyjściowe pochodzą z tego uruchomienia. Pomaga już opanowanie funkcji i pętli, które omawiamy w naszym samouczku Pythona dla początkujących.

Co to jest generator?

Generator to specjalny rodzaj iteratora, który tworzysz, pisząc funkcję za pomocą yield. Każda funkcja zawierająca yield automatycznie staje się funkcją generatora, a jej wywołanie daje obiekt generatora zamiast uruchamiania kodu.

def count_up(limit):
    print("starting")
    n = 1
    while n <= limit:
        yield n
        n += 1
    print("finished")

gen = count_up(2)
print(gen)

# <generator object count_up at 0x...>

Zauważ, że komunikat „start” nie został wydrukowany. Wywołanie funkcji spowodowało jedynie utworzenie generatora. Nic w środku jeszcze się nie uruchomiło.

Jak działa wydajność, krok po kroku

Każde wywołanie next() uruchamia funkcję aż do osiągnięcia yield, przekazuje tę wartość i zawiesza się w miejscu. Następne wywołanie zostanie wznowione zaraz po tej linii, z nienaruszoną każdą zmienną lokalną.

print(next(gen))
# starting
# 1

print(next(gen))
# 2

next(gen)
# finished
# StopIteration

Uważnie postępuj zgodnie z wynikami. „start” pojawia się tylko na pierwszym next(). W drugim przypadku wykonywanie jest kontynuowane od miejsca, w którym zostało zatrzymane, więc n jest nadal dostępne i wynosi 2. W trzecim przypadku pętla się kończy, następuje wypisanie komunikatu „zakończonego”, funkcja kończy się, a Python podnosi StopIteration, aby zasygnalizować, że nic już nie zostało.

Sam rzadko dzwonisz do next(). Pętla for robi to za Ciebie i obsługuje StopIteration cicho, podobnie jak list(), sum() i podobne funkcje:

print(list(count_up(3)))

# starting
# finished
# [1, 2, 3]

zysk vs zwrot

Aspekt wrócić plon
Co to robi Kończy funkcję Wstrzymuje funkcję
Wytworzone wartości Jeden wynik Seria, jedna po drugiej
Stan lokalny Odrzucony Zachowane pomiędzy wartościami
Kiedy kod działa Natychmiast po wezwaniu Tylko wtedy, gdy żądana jest wartość
Pamięć wielu wartości Trzeba je wszystkie trzymać Trzyma jeden na raz

To opóźnienie jest znane jako leniwa ocena: wartości są opracowywane tylko wtedy, gdy jest to potrzebne. Zwykły return wewnątrz generatora po prostu go kończy, co uruchamia StopIteration.

Zmierzona różnica w pamięci

Porównaliśmy listę miliona kwadratów z równoważnym generatorem, używając sys.getsizeof:

import sys

squares_list = [n * n for n in range(1_000_000)]
squares_gen = (n * n for n in range(1_000_000))

print(sys.getsizeof(squares_list), sys.getsizeof(squares_gen))

# 8448728 200

Własna struktura listy zajmowała około 8,4 MB, podczas gdy generator w naszym systemie zajmował zaledwie 200 bajtów. Rzeczywista różnica jest jeszcze większa, ponieważ getsizeof nie zlicza obiektów całkowitych na liście. Rozmiar generatora również pozostaje taki sam dla miliarda elementów, ponieważ przechowuje on tylko swój aktualny stan, a nie wyniki.

Istnieje jednak kompromis. Listę można ponownie wykorzystać, indeksować i mierzyć za pomocą len(). Generator nie oferuje żadnego z nich. Jeśli potrzebujesz dostępu losowego lub będziesz wykonywać pętlę kilka razy na małej ilości danych, lista jest prostsza. Zobacz nasz przewodnik po Lista Pythona vs Tuple vs Set, aby uzyskać więcej informacji na temat tych typów.

Wyrażenia generatora

W prostych przypadkach nie potrzebujesz funkcji. Wyrażenie generatora wygląda jak zrozumienie listowe z nawiasami okrągłymi zamiast kwadratowych:

squares = [n * n for n in range(5)]   # builds a list now
squares = (n * n for n in range(5))   # produces values on demand

print(sum(n * n for n in range(5)))

# 30

Gdy wyrażenie generatora jest jedynym argumentem funkcji, jak w sum(), możesz pominąć dodatkowe nawiasy. Jest to dobry zwyczaj w przypadku sum obejmujących duże ilości danych, ponieważ nie jest tworzona żadna lista tymczasowa.

Zbuduj potok danych

To tutaj generatory naprawdę świecą. Małe generatory można łączyć w łańcuchy, dzięki czemu każdy element przechodzi przez każdy etap, jeden po drugim:

def read_lines(lines):
    for line in lines:
        yield line.strip()

def only_errors(lines):
    for line in lines:
        if line.startswith("ERROR"):
            yield line

def messages(lines):
    for line in lines:
        yield line.split(" ", 1)[1]

log = ["INFO started\n", "ERROR db down\n",
       "ERROR disk full\n", "INFO stopped\n"]

print(list(messages(only_errors(read_lines(log)))))

# ['db down', 'disk full']

Każdy krok wykonuje jedną małą pracę i można go przetestować samodzielnie. W rzeczywistym użyciu zamień przykładową listę na otwarty plik, ponieważ obiekty plików można czytać linia po linii, jak pokazuje nasz przewodnik po obsłudze plików w Pythonie. Nawet wielogigabajtowy dziennik zostałby wówczas przesłany z bardzo małą ilością pamięci.

Wskazówka: Potok nie działa, dopóki coś go nie zużyje. Budowa łańcucha jest błyskawiczna. Przetwarzanie odbywa się tylko wtedy, gdy zapętlisz końcowy generator lub zawiniesz go w wywołaniu, takim jak list().

Nieskończone sekwencje

Lista nie może być nieskończona, ale generator może, ponieważ oblicza tylko następną wartość. Użyj itertools.islice, aby wziąć tyle, ile potrzebujesz:

import itertools

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

print(list(itertools.islice(fibonacci(), 8)))

# [0, 1, 1, 2, 3, 5, 8, 13]

Bądź jednak ostrożny: wywołanie list(fibonacci()) bez limitu będzie trwało, dopóki nie zniknie twoja pamięć. Ten sam wzorzec pasuje do generatorów identyfikatorów, pętli odpytywania i stronicowanych wyników API, w przypadku których następną stronę można pobrać dopiero wtedy, gdy bieżąca się skończy.

uzysk z i partii

yield from przekazuje inną iterację, oszczędzając zagnieżdżoną pętlę:

def chain_all(*groups):
    for group in groups:
        yield from group

print(list(chain_all([1, 2], (3, 4))))

# [1, 2, 3, 4]

Bardzo praktycznym wzorcem jest dzielenie danych na partie o stałym rozmiarze, na przykład w celu wstawienia tysiąca wierszy do bazy danych na raz:

def batches(items, size):
    batch = []
    for item in items:
        batch.append(item)
        if len(batch) == size:
            yield batch
            batch = []
    if batch:
        yield batch

print(list(batches(range(7), 3)))

# [[0, 1, 2], [3, 4, 5], [6]]

Ostatnia if batch zapewnia, że mniejsza grupa resztek nie zostanie utracona. Python 3.12 i nowsze wersje zawierają także itertools.batched, który wykonuje podobne zadanie.

wyślij, zamknij i wyczyść

Generatory mogą również odbierać wartości. send() wznawia generator, a wartość staje się wynikiem wyrażenia yield. Najpierw musisz zadzwonić do next(), aby dotrzeć do pierwszego yield:

def running_total():
    total = 0
    while True:
        value = yield total
        total += value

t = running_total()
next(t)
print(t.send(10), t.send(5))

# 10 15

Większość ludzi rzadko tego potrzebuje. Częściej spotykanym problemem jest sprzątanie. Jeśli zatrzymasz się wcześniej, blok finally będzie nadal działać, gdy generator zostanie zamknięty:

def tracked():
    try:
        yield 1
        yield 2
    finally:
        print("cleanup ran")

tr = tracked()
next(tr)
tr.close()

# cleanup ran

Pytest wykorzystuje ten sam pomysł: urządzenie z yield uruchamia później czyszczenie, jak pokazuje nasz samouczek pytest. Podobnie dekorator contextmanager biblioteki standardowej zamienia generator w blok with.

Generatory na zajęciach

Napisanie metody __iter__ jako generatora to najłatwiejszy sposób na umożliwienie iteracji własnej klasy. Dodatkową zaletą jest to, że każda pętla otrzymuje nowy generator, dzięki czemu obiekt można zapętlać wielokrotnie:

class Countdown:
    def __init__(self, start):
        self.start = start

    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1

c = Countdown(3)
print(list(c), list(c))

# [3, 2, 1] [3, 2, 1]

Kiedy używać generatora

Użyj generatora, kiedy Użyj listy, kiedy
Dane są duże lub ich rozmiar jest nieznany Dane są małe i mieszczą się w pamięci
Przedmioty przetwarzasz raz, w określonej kolejności Zapętlasz kilka razy
Możesz zatrzymać się wcześniej Potrzebujesz len(), indeksowania lub krojenia
Czytasz strumień lub pliki Musisz posortować lub odwrócić dane
Budujesz rurociąg Prostota jest ważniejsza niż pamięć

Generator nie zawsze jest ogólnie szybszy. Jeśli ostatecznie użyjesz każdej wartości, całkowita praca będzie podobna, a zrozumienie listy może być nawet szybsze w przypadku małych danych, ponieważ wiąże się z mniejszym obciążeniem na element. Główne korzyści to wykorzystanie pamięci i szybsze uzyskiwanie pierwszych wyników.

Typowe błędy

Ponowne wykorzystanie wyczerpanego generatora

To dopada prawie każdego w pewnym momencie. Generator pamięta, że jest skończony:

g = count_up(2)
print(list(g), list(g))

# [1, 2] []

Drugie wywołanie otrzymuje pustą listę bez żadnego błędu. Wywołaj funkcję ponownie, aby uzyskać nowy generator, lub użyj podejścia klasowego pokazanego powyżej.

Oczekiwanie na natychmiastowe uruchomienie kodu

def noisy():
    print("body runs")
    yield 1

n = noisy()        # nothing is printed
print(next(n))     # body runs, then 1

Sprawdzanie argumentów na górze generatora nie jest uruchamiane aż do pierwszego next(), więc błędy mogą pojawić się daleko od wywołania, które je spowodowało.

Inne pułapki

  • Wywołanie len() lub indeksowanie. Generatory nie obsługują żadnego z nich i zgłaszają TypeError.
  • Zbyt szybkie zamknięcie pliku. Zwrócenie generatora z wnętrza bloku with może oznaczać zamknięcie pliku przed odczytaniem jakiejkolwiek linii. Umieść yield wewnątrz bloku with.
  • Zawijanie wszystkiego w list(). To eliminuje korzyści związane z pamięcią.
  • Używanie jednej do małych danych. Zwykła lista jest często jaśniejsza.

Podsumowanie

Generator to funkcja, która może się zatrzymać. Ten jeden pomysł zapewnia niskie zużycie pamięci, nieskończone sekwencje i czyste potoki. Aby zastosować to w praktyce, znajdź w kodzie miejsce, które tworzy dużą listę tylko po to, aby raz ją zapętlić i zamień ją w generator. Następnie spróbuj połączyć ze sobą dwa małe generatory w prawdziwym pliku dziennika.

Najczęstsze pytania

Co robi wydajność w Pythonie?

Wydajność generuje wartość i wstrzymuje funkcję, zachowując jej zmienne lokalne. Funkcja jest wznawiana od tego samego punktu, gdy żądana jest następna wartość.

Jaka jest różnica między generatorem a listą?

Lista przechowuje wszystkie swoje wartości w pamięci jednocześnie. Generator oblicza każdą wartość na żądanie, zużywa mało pamięci i zwykle można ją wykorzystać tylko raz.

Jaka jest różnica między iteratorem a generatorem?

Każdy generator jest iteratorem. Generator to po prostu najprostszy sposób na jego napisanie, ponieważ Python tworzy za Ciebie metody __iter__ i __next__.

Czy możesz dwukrotnie zapętlić generator?

Nie. Po wyczerpaniu nie daje nic więcej, bez zgłaszania błędu. Utwórz nowy generator, wywołując funkcję ponownie, lub zapisz wyniki na liście.

Czy generatory są szybsze niż listy?

Nie zawsze. Zużywają znacznie mniej pamięci i szybciej dostarczają pierwszy wynik, ale zrozumienie listy może przebiegać nieco szybciej, jeśli w całości zużywa się małe ilości danych.

Ucz się dalej

Pracuj z Stack Quora

Potrzebujesz pomocy przy testowaniu, WordPressie lub SEO?

Pomagamy zespołom dostarczać lepsze oprogramowanie i rozwijać ich witryny. Powiedz nam, nad czym pracujesz, a my zaproponujemy jasny następny krok.

Czytaj dalej

Więcej poradników na testowanie, sztuczna inteligencja i nowoczesny rozwój

Podobał Ci się ten artykuł? Wybierz temat i przejdź bezpośrednio do naszych najnowszych tutoriali.

Sztuczna inteligencja

Wyjaśnienie halucynacji AI: dlaczego modele językowe tworzą rzeczy i jak je zmniejszyć

Wyjaśniono halucynacje AI: czym są, dlaczego powstają w modelach językowych, typowe typy, jak je zredukować za pomocą uziemień i podpowiedzi oraz pr…

Przeczytaj artykuł →
DevOps

Wyjaśnienie zmiennych środowiskowych: pliki .env, sekrety i konfiguracja wykonana prawidłowo

Zmienne środowiskowe wyjaśnione na przykładach Pythona, które przeprowadziliśmy: ich odczytywanie i ustawianie, dlaczego każda wartość jest ciągiem …

Przeczytaj artykuł →
Tworzenie oprogramowania

Wyjaśnienie ograniczenia szybkości interfejsu API: algorytmy, nagłówki i obsługa błędów 429

Ograniczanie szybkości API wyjaśnione za pomocą przeprowadzonej przez nas symulacji: zasobnik tokenów, stałe okno, przesuwane okno, 429 odpowiedzi, …

Przeczytaj artykuł →
Automatyzacja testów

Wyśmiewanie w Pythonie: unittest.mock, patch i błędy, które sprawiają, że testy kłamią

Wyśmiewanie w Pythonie za pomocą unittest.mock, przetestowane: Mock i zwracana wartość, efekt uboczny, łatanie tam, gdzie używana jest nazwa, specyf…

Przeczytaj artykuł →
Testowanie oprogramowania

Wyjaśnienie zasięgu kodu: pokrycie linii i oddziału oraz co naprawdę oznacza 100%

Wyjaśnienie pokrycia kodu za pomocą prawdziwego pliku cover.py: pokrycie linii, gałęzi i warunków, przeczytanie raportu, dlaczego 100% pokrycia może…

Przeczytaj artykuł →
Bazy danych i SQL

DELETE vs TRUNCATE vs DROP w SQL: różnice, wycofywanie i kiedy używać każdego z nich

DELETE vs TRUNCATE vs DROP wyjaśnione za pomocą przetestowanego SQL: co każdy usuwa, wycofywanie, wyzwalacze, resetowanie tożsamości, prędkość i czy…

Przeczytaj artykuł →
Bazy danych i SQL

Wyjaśnienie widoków SQL: tworzenie, używanie i unikanie pułapek wraz z przykładami

Widoki SQL wyjaśnione za pomocą zapytań, które uruchomiliśmy: tworzenie widoku, wyniki na żywo, ukrywanie kolumn, wydajność, widoki z możliwością ak…

Przeczytaj artykuł →
Tworzenie oprogramowania

Interfejs TypeScript a typ: rzeczywiste różnice, testowane za pomocą kompilatora

Interfejs TypeScript a typ, sprawdzony z prawdziwym kompilatorem: co oba mogą zrobić, co tylko jeden, łączenie deklaracji, rozszerzenia a przecięcia…

Przeczytaj artykuł →
Tworzenie oprogramowania

Python *args i **kwargs wyjaśnione: elastyczne funkcje z przejrzystymi przykładami

Python *args i **kwargs wyjaśnione za pomocą uruchomionego przez nas kodu: pakowanie i rozpakowywanie, kolejność parametrów, argumenty zawierające t…

Przeczytaj artykuł →
Tworzenie oprogramowania

Funkcje Lambda w Pythonie: składnia, rzeczywiste zastosowania i kiedy ich nie używać

Funkcje lambda w Pythonie wyjaśnione za pomocą uruchomionego przez nas kodu: składnia, sortowanie za pomocą klucza, mapa i filtr, domknięcia, pułapk…

Przeczytaj artykuł →
Sztuczna inteligencja

Kim są agenci AI? Jak one działają, dzięki prostej pętli agenta, którą możesz uruchomić

Kim są agenci AI? Prosty przewodnik po pętli agenta, narzędziach, pamięci i poręczach, z małą pętlą w Pythonie, którą uruchomiliśmy, prawdziwymi zag…

Przeczytaj artykuł →
DevOps

Monitorowanie a obserwowalność: prawdziwa różnica z logami, wskaźnikami i śladami

Wyjaśnienie monitorowania a obserwowalności: znane i nieznane awarie, dzienniki, metryki i ślady, opis incydentu przy kasie, złote sygnały, OpenTele…

Przeczytaj artykuł →
DevOps

Woluminy platformy Docker a mocowania powiązań: utrwalanie danych kontenera we właściwy sposób

Wyjaśniono woluminy Dockera a montowanie powiązań: dlaczego dane kontenera znikają, nazwane woluminy, montowanie powiązań, tmpfs, przykłady tworzeni…

Przeczytaj artykuł →
Automatyzacja testów

TDD vs BDD: kluczowe różnice, jedna funkcja zbudowana w obie strony i kiedy używać każdej z nich

TDD vs BDD wyjaśnione za pomocą jednej funkcji rabatowej zbudowanej w obie strony: refaktoryzacja czerwono-zielona z uruchomionym przez nas kodem, s…

Przeczytaj artykuł →
Testowanie oprogramowania

Testowanie akceptacyjne użytkownika (UAT): proces, lista kontrolna i rzeczywiste przykłady

Wyjaśniono testy akceptacyjne użytkownika: kto je przeprowadza, czym różni się od kontroli jakości, siedmioetapowy proces, szablon przypadku testowe…

Przeczytaj artykuł →
Bazy danych i SQL

Wyjaśnienie wartości SQL NULL: IS NULL, COALESCE, NULLIF i pułapki, których należy unikać

Wartości SQL NULL wyjaśnione za pomocą zapytań, które wykonaliśmy: logika trójwartościowa, IS NULL, COALESCE, NULLIF, w jaki sposób agregacje, łącze…

Przeczytaj artykuł →
Bazy danych i SQL

Instrukcja SQL CASE WHEN: składnia, rzeczywiste przykłady i typowe błędy

SQL CASE WHEN wyjaśnione za pomocą zapytań, które uruchomiliśmy: proste i przeszukiwane formularze, kolejność warunków, elementy przestawne z SUM, s…

Przeczytaj artykuł →
Tworzenie oprogramowania

Co to jest JWT? Tokeny internetowe JSON wyjaśnione na przykładzie działania

Co to jest JWT? Wyjaśniono tokeny sieciowe JSON: trzy części, token zbudowany i zmodyfikowany w uruchomionym przez nas kodzie, JWT a sesje, pamięć m…

Przeczytaj artykuł →
Tworzenie oprogramowania

Wyjaśnienie pętli zdarzeń JavaScript: stos wywołań, mikrozadania i liczniki czasu z przykładami

Pętla zdarzeń JavaScript wyjaśniona za pomocą uruchomionego przez nas kodu: stos wywołań, kolejki zadań i mikrozadań, dlaczego setTimeout 0 nie jest…

Przeczytaj artykuł →
Tworzenie oprogramowania

Wyjaśnienie generatorów Pythona i wydajności: oszczędzaj pamięć dzięki prawdziwym przykładom

Generatory Pythona i wydajność wyjaśnione za pomocą uruchomionego przez nas kodu: sposób wstrzymywania i wznawiania, mierzone oszczędności pamięci, …

Przeczytaj artykuł →
Sztuczna inteligencja

Czym są osady? Wyjaśnienie wektorów i podobieństwa cosinusa

Czym są osadzania? Prosty przewodnik po wektorach, podobieństwie cosinusów i wyszukiwaniu semantycznym, z małym przykładem, który przeprowadziliśmy,…

Przeczytaj artykuł →
DevOps

Wdrożenie niebiesko-zielone a kanaryjskie: różnice, kompromisy i sposób wyboru

Wyjaśniono wdrożenie Blue-Green vs Canary: sposób działania każdego z nich, koszty, ryzyko i wycofywanie zmian, przetestowane przez nas stabilne łąc…

Przeczytaj artykuł →
Automatyzacja testów

Niestabilne testy: co je powoduje i jak je naprawić na dobre

Niestabilne testy wyjaśnione w przeprowadzonych przez nas demonstracjach: główne przyczyny, sposób potwierdzenia i zdiagnozowania jednej z nich, odp…

Przeczytaj artykuł →
Testowanie oprogramowania

Load Testing vs Stress Testing: Differences, Metrics and k6 Examples

Wyjaśniono testy obciążenia i testy warunków skrajnych: co dowodzi każdy z nich, testy szczytowe i nasiąkania, istotne metryki, dlaczego średnie wpr…

Przeczytaj artykuł →
Testowanie oprogramowania

Przypadek testowy a scenariusz testowy: kluczowe różnice, przykłady i kiedy stosować każdy z nich

Wyjaśnienie przypadków testowych i scenariuszy testowych: jasne definicje, tabela obok siebie, jedna funkcja resetowania hasła zaczerpnięta ze scena…

Przeczytaj artykuł →
Bazy danych i SQL

Optymalizacja zapytań SQL: czytaj plany wykonania i napraw powolne zapytania z synchronizacją

Optymalizacja zapytań SQL przetestowana na milionie wierszy: przeczytaj plany wykonania, dodaj odpowiednie indeksy, napraw filtry niesargowalne, uży…

Przeczytaj artykuł →
Bazy danych i SQL

SQL UNION vs UNION ALL: różnice, przykłady i testowana wydajność

SQL UNION vs UNION ALL wyjaśnione na podstawie zapytań, które uruchomiliśmy: duplikaty, obie reguły są zgodne, prawdziwy błąd danych, zmierzona wyda…

Przeczytaj artykuł →
Tworzenie oprogramowania

Samouczek API pobierania JavaScript: GET, POST, obsługa błędów i limity czasu

Samouczek JavaScript Fetch API testowany na serwerze lokalnym: GET i POST, dlaczego 404 nie rzuca, pomocnik wielokrotnego użytku, ciągi zapytań, prz…

Przeczytaj artykuł →
Tworzenie oprogramowania

Obsługa plików w Pythonie: odczyt, zapis, CSV, JSON i pathlib z przykładami

Obsługa plików w Pythonie wyjaśniona za pomocą uruchomionego przez nas kodu: tryby otwarte, czytanie dużych plików, CSV, JSON, pathlib, pliki binarn…

Przeczytaj artykuł →
Tworzenie oprogramowania

Klasy i obiekty Pythona: samouczek OOP z prawdziwymi przykładami

Klasy i obiekty Pythona objaśnione za pomocą uruchomionego przez nas kodu: __init__, metody, atrybuty klas i instancji, dziedziczenie, właściwości, …

Przeczytaj artykuł →
Sztuczna inteligencja

Co to jest model dużego języka? Jak działają LLM, wyjaśnione bez szumu

Co to jest model dużego języka? LLM wyjaśnione prostym językiem: tokeny, osadzanie, uwaga, szkolenie, temperatura i okna kontekstowe, mała demonstra…

Przeczytaj artykuł →
DevOps

Polecenia systemu Linux dla początkujących: niezbędny przewodnik po terminalu z przykładami

Polecenia systemu Linux dla początkujących, pogrupowane według zadań: nawigacja, pliki, przeglądanie logów, grep, potoki, uprawnienia, procesy i arc…

Przeczytaj artykuł →
Automatyzacja testów

Selenium kontra dramaturg: kluczowe różnice, porównanie kodu i wybór

Porównanie Selenium i Playwright: architektura, oczekiwanie, obsługa przeglądarki i języka, debugowanie, ten sam test w obu narzędziach, porady doty…

Przeczytaj artykuł →
Testowanie oprogramowania

Partycjonowanie równoważne: technika projektowania testów wyjaśniona na praktycznych przykładach

Wyjaśniono podział równoważny: jak znaleźć prawidłowe i nieprawidłowe partycje, sprawdzone przykłady dla grup wiekowych, hasła i kupony, przeprowadz…

Przeczytaj artykuł →
Testowanie oprogramowania

Ważność a priorytet w testowaniu oprogramowania: różnice, macierz i rzeczywiste przykłady

Wyjaśniono ważność a priorytet w testowaniu oprogramowania: jasne definicje, czterokierunkowa macierz z przykładami, skale poziomów, kto ustawia każ…

Przeczytaj artykuł →
Bazy danych i SQL

Samouczek dotyczący podzapytań SQL: typy, skorelowane podzapytania i ISTNIEJĄ z przykładami

Samouczek dotyczący podzapytań SQL z zapytaniami, które wykonaliśmy: podzapytania skalarne, IN, FROM i SELECT, podzapytania skorelowane, EXISTS, puł…

Przeczytaj artykuł →
Bazy danych i SQL

Wyjaśnienie transakcji SQL i ACID: COMMIT, ROLLBACK i poziomy izolacji

Transakcje SQL i ACID wyjaśnione za pomocą wykonanego przez nas przelewu bankowego: COMMIT, ROLLBACK, punkty zapisu, blokady, poziomy izolacji, brud…

Przeczytaj artykuł →
Tworzenie oprogramowania

Git merge vs rebase: różnice, przykłady i kiedy czego używać

Git merge vs rebase wyjaśnione na prawdziwym wyniku logu, z tabelą porównawczą, złotą zasadą rebase i bezpiecznym procesem pracy zespołu.

Przeczytaj artykuł →
Tworzenie oprogramowania

Python list comprehension: składnia, przykłady i kiedy jej unikać

Python list comprehension wyjaśnione: składnia, filtrowanie, if-else, zagnieżdżone pętle, zmierzone czasy i sytuacje, w których zwykła pętla for jes…

Przeczytaj artykuł →
Bazy danych i SQL

SQL JOIN – wyjaśnienie z przykładami (Inner, Left, Right, Full)

Zrozum złączenia SQL krok po kroku na przykładowych tabelach i zapytaniach: INNER, LEFT, RIGHT, FULL, CROSS i SELF, plus typowe błędy i wskazówki wy…

Przeczytaj artykuł →