<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[DevPython]]></title><description><![CDATA[Programowanie w Python i Django (dla zabieganych).]]></description><link>https://blog.devpython.pl/</link><image><url>https://blog.devpython.pl/favicon.png</url><title>DevPython</title><link>https://blog.devpython.pl/</link></image><generator>Ghost 4.3</generator><lastBuildDate>Tue, 05 May 2026 16:48:51 GMT</lastBuildDate><atom:link href="https://blog.devpython.pl/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Gra w liczby]]></title><description><![CDATA[Dzisiejszy wpis omawia grę Sliding Puzzle. Gra ta ma proste reguły. Polega ona na tym, aby wykorzystać lukę do przesuwania puzzli, tak aby uzyskać liczby w rosnącej kolejności.]]></description><link>https://blog.devpython.pl/gra-w-liczby/</link><guid isPermaLink="false">68fe3f76ac2460016951b645</guid><category><![CDATA[python]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Sun, 03 May 2026 15:59:54 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/10/grawliczby_blogZaso-b-1@1.5x.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/10/grawliczby_blogZaso-b-1@1.5x.png" alt="Gra w liczby"><p>Dzisiejszy wpis omawia gr&#x119; <em>Sliding Puzzle</em>. Gra ta ma proste regu&#x142;y. Polega ona na tym, aby wykorzysta&#x107; luk&#x119;&#xA0;do przesuwania puzzli, tak aby uzyska&#x107; liczby w rosn&#x105;cej kolejno&#x15B;ci.</p><p>Dla przyk&#x142;adu, gra mo&#x17C;e mie&#x107; na pocz&#x105;tku nast&#x119;puj&#x105;cy stan:</p><figure class="kg-card kg-image-card"><img src="https://blog.devpython.pl/content/images/2025/10/Zrzut-ekranu-2025-10-26-o-16.41.37.png" class="kg-image" alt="Gra w liczby" loading="lazy" width="1032" height="996" srcset="https://blog.devpython.pl/content/images/size/w600/2025/10/Zrzut-ekranu-2025-10-26-o-16.41.37.png 600w, https://blog.devpython.pl/content/images/size/w1000/2025/10/Zrzut-ekranu-2025-10-26-o-16.41.37.png 1000w, https://blog.devpython.pl/content/images/2025/10/Zrzut-ekranu-2025-10-26-o-16.41.37.png 1032w" sizes="(min-width: 720px) 720px"></figure><p>W tym przypadku mo&#x17C;emy wykona&#x107;&#xA0;3 mo&#x17C;liwe ruchy: </p><ul><li>przesun&#x105;&#x107; 6 w d&#xF3;&#x142;,</li><li>przesun&#x105;&#x107; 3 w g&#xF3;r&#x119;,</li><li>przesun&#x105;&#x107; 2 w prawo.</li></ul><p>Gdy wybierzemy&#xA0;3, wtedy plansza b&#x119;dzie wygl&#x105;da&#x107; nast&#x119;puj&#x105;co:</p><figure class="kg-card kg-image-card"><img src="https://blog.devpython.pl/content/images/2025/10/Zrzut-ekranu-2025-10-26-o-16.45.31.png" class="kg-image" alt="Gra w liczby" loading="lazy" width="988" height="980" srcset="https://blog.devpython.pl/content/images/size/w600/2025/10/Zrzut-ekranu-2025-10-26-o-16.45.31.png 600w, https://blog.devpython.pl/content/images/2025/10/Zrzut-ekranu-2025-10-26-o-16.45.31.png 988w" sizes="(min-width: 720px) 720px"></figure><p>Teraz mo&#x17C;emy&#xA0;wykona&#x107; ruch 3 w d&#xF3;&#x142; albo 8 w prawo. </p><h3 id="implementacja">Implementacja</h3><p>Lista list bardzo dobrze nadaje si&#x119; do reprezentowania planszy:</p><pre><code>    board = [
        [1, 0, 2],
        [3, 4, 5],
        [6, 7, 8]
    ]</code></pre><p>Zero oznacza puste pole, od kt&#xF3;rego wychodzimy podczas rozgrywki.</p><p>Do sprawdzenia, czy dwie pozycje s&#x105; s&#x105;siednie, mo&#x17C;emy wykorzysta&#x107; &#xA0;funkcj&#x119;&#xA0;<code>connected</code>, kt&#xF3;ra zwraca warto&#x15B;&#x107; logiczn&#x105;:</p><pre><code>def connected(a, b):
    row_diff = abs(a[0] - b[0])
    col_diff = abs(a[1] - b[1])
    return row_diff + col_diff == 1
</code></pre><p>Funkcja ta liczy dystans mi&#x119;dzy pozycjami i je&#x15B;li uzyskana warto&#x15B;&#x107; wynosi 1, wtedy dwie pozycje s&#x105; s&#x105;siednie.</p><p>Operowanie samymi wsp&#xF3;&#x142;rz&#x119;dnymi pozycji nie jest wygodne. W tej sytuacji znacznie &#x142;atwiej jest odwo&#x142;a&#x107; si&#x119; do okre&#x15B;lonej liczby, np. &#xA0;3, zamiast do jej pozycji, kt&#xF3;ra obecnie wynosi [1, 2]. Takie odwo&#x142;anie staje si&#x119; mo&#x17C;liwe dzi&#x119;ki funkcji <code>get_pos</code>:</p><pre><code>def get_pos(board, x):
    for row_index, row in enumerate(board):
        for col_index, value in enumerate(row):
            if value == x:
                return row_index, col_index
    return None</code></pre><p>Powy&#x17C;sza funkcja przyjmuje plansz&#x119; i szukan&#x105; warto&#x15B;&#x107;, a jako wynik zwraca jej pozycj&#x119;. Teraz mo&#x17C;emy napisa&#x107; kod, kt&#xF3;ry oceni, czy obie warto&#x15B;ci zajmuj&#x105; s&#x105;siednie miejsca na planszy:</p><pre><code>print(connected(
  get_pos(board, 1),
  get_pos(board, 0)
))</code></pre><p>Po zweryfikowaniu czy pozycje s&#x105; s&#x105;siaduj&#x105;ce, czas najwy&#x17C;szy, by zapisa&#x107; funkcj&#x119;, kt&#xF3;ra dokona zamiany warto&#x15B;ci na wskazanych polach.</p><pre><code>def swap(board, a, b):
    v1 = board[a[0]][a[1]]
    v2 = board[b[0]][b[1]]
    board[a[0]][a[1]] = v2
    board[b[0]][b[1]] = v1</code></pre><p>Funkcja <code>swap</code> przyjmuje plansz&#x119; i dwie dowolne pozycje, w obr&#x119;bie kt&#xF3;rych ma doj&#x15B;&#x107; do zamiany warto&#x15B;ci.</p><p>Pozostaje jeszcze obs&#x142;uga u&#x17C;ytkownika. Chocia&#x17C; gra jest prosta, to na etapie wprowadzania warto&#x15B;ci, mo&#x17C;na pope&#x142;ni&#x107;&#xA0;r&#xF3;&#x17C;ne b&#x142;&#x119;dy, kt&#xF3;re warto obs&#x142;u&#x17C;y&#x107;&#xA0;w tworzonym programie.</p><pre><code>def ask_user(board):
    zero_pos = get_pos(board, 0)
    while True:
        try:
            nr = int(input(&apos;&gt; &apos;))
        except ValueError:
            print(&apos;number is required&apos;)
            continue

        user_pos = get_pos(board, nr)
        if user_pos is None:
            print(&apos;required number between 1 and 8&apos;)
            continue

        if not connected(zero_pos, user_pos):
            print(&apos;wrong number, try again&apos;)
            continue

        return user_pos
</code></pre><p>Funkcja <code>ask_user</code> jest odporna na podanie znak&#xF3;w r&#xF3;&#x17C;nych od cyfr. Sprawdza dost&#x119;pno&#x15B;&#x107; pozycji na planszy i na sam koniec weryfikuje, czy wybrane pola s&#x105; s&#x105;siednie wzgl&#x119;dem siebie.</p><p>Do sprawdzenia, czy gra dobieg&#x142;a ko&#x144;ca, mo&#x17C;emy wykorzysta&#x107; dwie nast&#x119;puj&#x105;ce funkcje:</p><pre><code>def iter_board(board):
    for row in board:
        for value in row:
            yield value


def is_ordered(board):
    values = list(iter_board(board))
    return sorted(values) == values
</code></pre><p>Podczas gdy funkcja <code>is_ordered</code> odpowiada na pytanie, czy pola s&#x105; u&#x142;o&#x17C;one rosn&#x105;co, funkcja <code>iter_board</code> tworzy generator, kt&#xF3;ry umo&#x17C;liwia przej&#x15B;cie po dwuwymiarowej strukturze. To pozwala na wykorzystanie funkcji&#xA0;<code>sorted</code>.</p><p>Na sam koniec pozosta&#x142;a nam jeszcze funkcja <code>run game</code>, kt&#xF3;ra wykorzystuje wcze&#x15B;niej om&#xF3;wione funkcje.</p><pre><code>def run_game():
    board = [
        [1, 0, 2],
        [3, 4, 5],
        [6, 7, 8]
    ]
    while True:
        for row in board:
            print(row)

        user_pos = ask_user(board)
        zero_pos = get_pos(board, 0)
        swap(board, user_pos, zero_pos)

        if is_ordered(board):
            break

    for row in board:
        print(row)

run_game()</code></pre><p>Szczeg&#xF3;&#x142;owy opis ka&#x17C;dej z funkcji znajdziesz r&#xF3;wnie&#x17C; na moim kanale youtube. Linki do odcink&#xF3;w znajduj&#x105; si&#x119; poni&#x17C;ej:</p><ol><li><a href="https://www.youtube.com/watch?v=CI2qCsF5EI4">https://www.youtube.com/watch?v=CI2qCsF5EI4</a></li><li><a href="https://www.youtube.com/watch?v=bkVBoX0NSYY">https://www.youtube.com/watch?v=bkVBoX0NSYY</a></li><li><a href="https://www.youtube.com/watch?v=ZJrVPcPdlww&amp;t=3s">https://www.youtube.com/watch?v=ZJrVPcPdlww&amp;t=3s</a></li><li><a href="https://www.youtube.com/watch?v=ne-b6IW1bwI">https://www.youtube.com/watch?v=ne-b6IW1bwI</a></li><li><a href="https://www.youtube.com/watch?v=dhd1O237QMo">https://www.youtube.com/watch?v=dhd1O237QMo</a></li><li><a href="https://www.youtube.com/watch?v=RdSAOpNqpu8">https://www.youtube.com/watch?v=RdSAOpNqpu8</a></li></ol>]]></content:encoded></item><item><title><![CDATA[Zarządzanie stanem w Pythonie]]></title><description><![CDATA[W tym wpisie chciałbym przedstawić konstrukcje do zarządzania stanem jakie oferuje język Python.]]></description><link>https://blog.devpython.pl/zarzadzanie-stanem-w-pythonie/</link><guid isPermaLink="false">68502f39ac24600169518826</guid><category><![CDATA[python]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Sat, 18 Apr 2026 17:57:06 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2026/04/pioro_ksiazka.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2026/04/pioro_ksiazka.svg" alt="Zarz&#x105;dzanie stanem w Pythonie"><p>W tym wpisie chcia&#x142;bym przedstawi&#x107; konstrukcje do zarz&#x105;dzania stanem jakie oferuje j&#x119;zyk Python.</p><h3 id="funkcja">Funkcja</h3><p>Na pocz&#x105;tek warto poruszy&#x107; szczeg&#xF3;lny przypadek, jakim jest funkcja. Z za&#x142;o&#x17C;enia funkcja nie ma stanu wi&#x119;c nie powinna by&#x107;&#xA0;rozpatrywana pod k&#x105;tem jego zarz&#x105;dzania. Nale&#x17C;y jednak podkre&#x15B;li&#x107;, &#x17C;e istnieje wiele odst&#x119;pstw od tej regu&#x142;y, o ile funkcja opiera swoje dzia&#x142;anie:</p><ul><li>na korzystaniu z globalnych lub</li><li>posiada <a href="https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments">mutowalny domy&#x15B;lny argument</a> lub </li><li>powoduje efekty uboczne, np. zapisanie danych do pliku.</li></ul><p>W takich przypadkach mo&#x17C;emy powiedzie&#x107;, &#x17C;e funkcja rzeczywi&#x15B;cie zarz&#x105;dza stanem.</p><h3 id="domkni%C4%99cie">Domkni&#x119;cie</h3><p>Domkni&#x119;cie to funkcja, kt&#xF3;ra pami&#x119;ta zmienne z zakresu funkcji nadrz&#x119;dnej. Na pierwszy rzut oka wydaje si&#x119; ona trudniejszym poj&#x119;ciem do przyswojenia, ale w rzeczywisto&#x15B;ci tak nie jest.</p><p>W gruncie rzeczy chodzi tylko o hermetyzacj&#x119;&#xA0;stanu, czyli o utworzenie stanu w funkcji nadrz&#x119;dnej, aby tylko funkcja wewn&#x119;trzna mog&#x142;a nim zarz&#x105;dza&#x107;. To ograniczenie powoduje, &#x17C;e zmiana stanu jest mo&#x17C;liwa jedynie w obr&#x119;bie stworzonej funkcji.</p><p>W poni&#x17C;szym przyk&#x142;adzie dochodzi do zwr&#xF3;cenia nowej funkcji zar&#xF3;wno dla <em>c1</em> jak i <em>c2</em>. Ka&#x17C;da z tych funkcji pami&#x119;ta sw&#xF3;j w&#x142;asny licznik.</p><pre><code>def make_counter():
    i = 0
    def counter():
        nonlocal i
        result = i
        i += 1
        return result
    return counter

c1 = make_counter()
print(c1())  # 0
print(c1())  # 1
print(c1())  # 2

c2 = make_counter()
print(c2()) # 0</code></pre><p>Je&#x15B;li przyk&#x142;ad z licznikami wydaje Ci si&#x119; zbyt uproszczony, to zach&#x119;cam Ci&#x119; do analizy praktycznego przypadku z biblioteki <a href="https://github.com/SeleniumHQ/selenium/blob/trunk/py/selenium/webdriver/support/expected_conditions.py">Selenium</a>.</p><h3 id="generator">Generator</h3><p>Kolejn&#x105; konstrukcj&#x105; dost&#x119;pn&#x105; w Pythonie, kt&#xF3;ra &#x15B;wietnie sprawdza si&#x119;&#xA0;w zarz&#x105;dzaniu stanem jest generator. Jest to rozwi&#x105;zanie o charakterze po&#x15B;rednim mi&#x119;dzy domkni&#x119;ciem, a klas&#x105;. Temat generatora zosta&#x142; om&#xF3;wiony <a href="https://blog.devpython.pl/kiedy-przydaje-sie-generator/">w osobnym wpisie</a>. </p><p>Przyk&#x142;ad z u&#x17C;yciem generatora przedstawia analogiczny przypadek z odliczaniem:</p><pre><code>def make_counter():
    i = 0
    while True:
        yield i
        i += 1

c1 = make_counter()
print(next(c1))  # 0
print(next(c1))  # 1
print(next(c1))  # 2

c2 = make_counter()
print(next(c2))  # 0
</code></pre><p>Najwi&#x119;ksza r&#xF3;&#x17C;nica jaka jest pomi&#x119;dzy u&#x17C;yciem generatora, a domkni&#x119;cia to ilo&#x15B;&#x107; zapisanego kodu. W przypadku generatora jest go zdecydowanie mniej, co wi&#x119;cej ta konstrukcja jest &#x142;atwiejsza ni&#x17C; funkcje wy&#x17C;szego rz&#x119;du. Warto wspomnie&#x107;&#xA0;r&#xF3;wnie&#x17C;, &#x17C;e u&#x17C;ycie generatora cz&#x119;&#x15B;ciej b&#x119;dzie dotyczy&#x107; operacji zwi&#x105;zanych z iteracj&#x105;.</p><h3 id="klasa">Klasa</h3><p>Nast&#x119;pn&#x105; konstrukcj&#x105;, kt&#xF3;rej nie mog&#x142;o tu zabrakn&#x105;&#x107; jest klasa. Poni&#x17C;szy przyk&#x142;ad kodu r&#xF3;wnie&#x17C; przedstawia odliczanie.</p><pre><code>class Counter:
    
    def __init__(self):
        self._i = 0
        
    def next(self):
        result = self._i 
        self._i += 1
        return result

c1 = Counter()
print(c1.next())  # 0
print(c1.next())  # 1
print(c1.next())  # 2

c2 = Counter()
print(c2.next())  # 0</code></pre><p>Klasy, w przeciwie&#x144;stwie do domkni&#x119;cia czy generatora, charakteryzuje mo&#x17C;liwo&#x15B;&#x107;&#xA0;manipulowania stanem z poziomu wielu r&#xF3;&#x17C;nych metod. To sprawia, &#x17C;e klasy s&#x105; du&#x17C;o bardziej elastyczne w zakresie zarz&#x105;dzania stanem.</p><p>Specjalnym przypadkiem klasy, kt&#xF3;ry zas&#x142;uguje na uwag&#x119; jest funktor, czyli klasa, kt&#xF3;ra implementuje magiczn&#x105; metod&#x119; <strong><strong><strong>call</strong></strong>. </strong>Obiekty tej klasy mo&#x17C;na wywo&#x142;ywa&#x107; podobnie jak zwyk&#x142;e funkcje.</p><pre><code>class Counter:

    def __init__(self):
        self._i = 0

    def __call__(self, *args, **kwargs):
        result = self._i
        self._i += 1
        return result

c1 = Counter()
print(c1())  # 0
print(c1())  # 1
print(c1())  # 2

c2 = Counter()
print(c2())  # 0
</code></pre><p><strong>Podsumowanie</strong><br>Jedn&#x105; ze szczeg&#xF3;lnych zalet Pythona, kt&#xF3;r&#x105; bardzo ceni&#x119;, to mo&#x17C;liwo&#x15B;&#x107; korzystania z wielu r&#xF3;&#x17C;nych konstrukcji. Oczywi&#x15B;cie wi&#x119;kszo&#x15B;&#x107;, jak nie wszystkie przypadki zwi&#x105;zane ze stanem, mo&#x17C;na rozwi&#x105;za&#x107; z u&#x17C;yciem klas. To co jednak przemawia za alternatywami to fakt, &#x17C;e s&#x105; prostsze i kr&#xF3;tsze, a co za tym idzie &#x142;atwiejsze w zrozumieniu. &#xA0;</p><p></p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Krótkie wprowadzenie w funkcyjne programowanie]]></title><description><![CDATA[Funkcyjne programowanie (dalej FP) to paradygmat, który pomimo coraz większej popularności i coraz lepszego wsparcia w językach programowania, wciąż wydaje się nieoczywisty w codziennym programowaniu.]]></description><link>https://blog.devpython.pl/krotkie-wprowadzenie-w-funkcyjne-programowanie/</link><guid isPermaLink="false">68454856ac24600169517099</guid><category><![CDATA[paradygmaty]]></category><category><![CDATA[funkcyjne programowanie]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Sat, 01 Nov 2025 20:58:25 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/06/lambda12Zaso-b-28@1.5x.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/06/lambda12Zaso-b-28@1.5x.png" alt="Kr&#xF3;tkie wprowadzenie w funkcyjne programowanie"><p>Funkcyjne programowanie (dalej FP) to paradygmat, kt&#xF3;ry pomimo coraz wi&#x119;kszej popularno&#x15B;ci i coraz lepszego wsparcia w j&#x119;zykach programowania, wci&#x105;&#x17C; wydaje si&#x119; nieoczywisty w codziennym programowaniu.</p><p>Na t&#x119;&#xA0;kwesti&#x119; warto spojrze&#x107; szerzej, poniewa&#x17C; stosowanie dowolnego paradygmatu wi&#x105;&#x17C;e si&#x119; z przyj&#x119;ciem pewnych odg&#xF3;rnych zasad, kt&#xF3;re z jednej strony toruj&#x105; my&#x15B;lenie o problemach, a z drugiej strony ograniczaj&#x105; pole manewru. Innymi s&#x142;owy, oddajemy cz&#x119;&#x15B;&#x107; kontroli/w&#x142;adzy w zamian za wi&#x119;ksz&#x105; przewidywalno&#x15B;&#x107;. </p><p>W przypadku FP d&#x105;&#x17C;y si&#x119; do uzyskania kodu, kt&#xF3;ry nie zarz&#x105;dza stanem ani r&#xF3;wnie&#x17C; nie powoduje efekt&#xF3;w ubocznych w postaci dodatkowych dzia&#x142;a&#x144; takich jak np. zapisanie pliku czy wys&#x142;anie wiadomo&#x15B;ci. To co zaskakuje w tym podej&#x15B;ciu, to fakt, &#x17C;e z punktu widzenia u&#x17C;ytkownika, taki kod tak naprawd&#x119; nie robi nic u&#x17C;ytecznego. W takim razie, jaki jest pow&#xF3;d dla kt&#xF3;rego programi&#x15B;ci wybieraj&#x105; to podej&#x15B;cie?</p><p>Motywacj&#x105;&#xA0;do stosowania FP s&#x105; korzy&#x15B;ci wynikaj&#x105;ce z <strong>oddzielenia oblicze&#x144; od pozosta&#x142;ych akcji</strong> jakie maj&#x105; miejsce w aplikacji.</p><p>Je&#x15B;li funkcja tylko oblicza wynik, bez wywo&#x142;ywania dodatkowych efekt&#xF3;w, wtedy jest &#x142;atwiejsza w czytaniu oraz testowaniu. Dzieje si&#x119;&#xA0;tak, poniewa&#x17C; funkcja zawsze obliczy ten sam wynik dla tych samych danych wej&#x15B;ciowych . </p><p>Sp&#xF3;jrzmy na poni&#x17C;szy kod, kt&#xF3;ry rysuje siatk&#x119; na ekranie.</p><p>Mo&#x17C;emy wtedy:</p><ul><li>bezpo&#x15B;rednio wywo&#x142;a&#x107; funkcje odpowiedzialne za rysowanie,</li><li>albo zgodnie z FP utworzy&#x107; funkcj&#x119;, kt&#xF3;ra b&#x119;dzie wolna od efekt&#xF3;w.</li></ul><p>Przedstawiony kod oblicza w pierwszej kolejno&#x15B;ci linie:</p><pre><code class="language-Python">def grid_lines(pos, width, height, rows, cols):
    x, y = pos
    cell_width = width // cols
    cell_height = height // rows

    for row_y in range(rows + 1):
        line_start_pos = (x, y + cell_height * row_y)
        line_end_pos = (x + width, y + cell_height * row_y)
        yield line_start_pos, line_end_pos

    for col_x in range(cols + 1):
        line_start_pos = (x + cell_width * col_x, y)
        line_end_pos = (x + cell_width * col_x, y + height)
        yield line_start_pos, line_end_pos</code></pre><p>by nast&#x119;pnie u&#x17C;y&#x107; t&#x119;&#xA0;funkcj&#x119; w nast&#x119;puj&#x105;cy spos&#xF3;b:</p><pre><code>for start_pos, end_pos in grid_lines(
    pos=(x, y),
    width=self.CELL_SIZE * self.CELL_COLS,
    height=self.CELL_SIZE * self.CELL_ROWS,
    rows=self.CELL_ROWS,
    cols=self.CELL_COLS
):
    pygame.draw.line(self._screen, color, start_pos, end_pos)
</code></pre><p>Docelowe u&#x17C;ycie funkcji wygl&#x105;da teraz trywialnie, poniewa&#x17C; &#xA0;<code>grid_lines</code> odpowiada za przeprowadzenie oblicze&#x144;.</p><hr><p>Niestety stosowanie FP nie zawsze ma sens. </p><p>Gdyby funkcja <code>grid_lines</code> w j&#x119;zyku Python, przy ka&#x17C;dym wywo&#x142;aniu zamiast generatora, zwraca&#x142;a list&#x119;&#xA0;krotek, w&#xF3;wczas takie podej&#x15B;cie by&#x142;oby niepraktyczne z punktu widzenia wydajno&#x15B;ci.</p>]]></content:encoded></item><item><title><![CDATA[Generator pairwise]]></title><description><![CDATA[Generator na przykładzie pairwise.]]></description><link>https://blog.devpython.pl/generator-pairwise/</link><guid isPermaLink="false">6857d30aac24600169519cb4</guid><category><![CDATA[python]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Sun, 19 Oct 2025 17:24:16 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/06/gen2Zaso-b-3.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/06/gen2Zaso-b-3.svg" alt="Generator pairwise"><p>We wpisie <a href="https://blog.devpython.pl/kiedy-przydaje-sie-generator/">Kiedy przydaje si&#x119; generator</a>, zosta&#x142; poruszony temat zwi&#x105;zany z wyodr&#x119;bnianiem dzia&#x142;a&#x144; z p&#x119;tli, kt&#xF3;ry chcia&#x142;bym tutaj rozszerzy&#x107; o kolejny przyk&#x142;ad.</p><p>Sp&#xF3;rzmy na poni&#x17C;szy fragment kodu:</p><!--kg-card-begin: html--><pre>
for i in range(len(numbers) - 1):
    print(numbers[i], numbers[i+1])
</pre><!--kg-card-end: html--><p>P&#x119;tla skupia si&#x119; na wy&#x15B;wietleniu element&#xF3;w z przyk&#x142;adowej listy <code>[1, 2, 3, 4, 5]</code> w spos&#xF3;b nast&#x119;puj&#x105;cy:</p><pre><code>1 2
2 3
3 4
4 5</code></pre><p>Tutaj mo&#x17C;na przygotowa&#x107; generator, kt&#xF3;ry wyodr&#x119;bni cz&#x119;&#x15B;&#x107; zwi&#x105;zan&#x105; z indeksami. </p><!--kg-card-begin: html--><pre>
def pairwise(xs):
    for i in range(len(xs) - 1):
        yield xs[i], xs[i+1]
</pre><!--kg-card-end: html--><p>Zapis tego generatora b&#x119;dzie tak wygl&#x105;da&#x107;:</p><!--kg-card-begin: html--><pre>
for a, b in pairwise(numbers):
    print(a, b)
</pre><!--kg-card-end: html--><p>Ta p&#x119;tla jest nie tylko kr&#xF3;tsza, ale r&#xF3;wnie&#x17C;, co warte podkre&#x15B;lenia, ukrywa prac&#x119; z indeksami. Dzi&#x119;ki temu zabiegowi kod tej p&#x119;tli staje si&#x119; czytelniejszy, poniewa&#x17C; nieistotne szczeg&#xF3;&#x142;y zostaj&#x105; zepchni&#x119;te na dalszy plan.</p><p>Podsumowuj&#x105;c, gdyby <code>pairwise</code> by&#x142; funkcj&#x105;, wtedy jej zastosowanie nie by&#x142;oby zasadne, poniewa&#x17C; z ka&#x17C;dym jej u&#x17C;yciem, tu&#x17C; przed wykonaniem p&#x119;tli, funkcja musia&#x142;aby wykona&#x107; si&#x119; w ca&#x142;o&#x15B;ci.</p>]]></content:encoded></item><item><title><![CDATA[Ile jeszcze?]]></title><description><![CDATA[Pytanie, które powinno być dla nas sygnałem ostrzegawczym.]]></description><link>https://blog.devpython.pl/ile-jeszcze/</link><guid isPermaLink="false">674b3871434bc2017158d3bf</guid><category><![CDATA[metody]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Wed, 10 Sep 2025 15:38:05 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2024/12/blog_Zaso-b-1@2x.png" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2024/12/blog_Zaso-b-1@2x.png" alt="Ile jeszcze?"><p>Nauka trudniejszych rzeczy niesie ze sob&#x105; zawsze nowe szanse i wyzwania. B&#x119;d&#x105;c na jej samym pocz&#x105;tku, niezwykle &#x142;atwo wyobrazi&#x107; sobie jak wiele mo&#x17C;e zmieni&#x107; nowa umiej&#x119;tno&#x15B;&#x107;. Z tak&#x105;&#xA0;motywacj&#x105;&#xA0;niemal wszystko wydaje si&#x119; mo&#x17C;liwe. Mo&#x17C;na wtedy poczu&#x107;, &#x17C;e jest si&#x119; gotowym na maraton. </p><p>Jednak z czasem nadchodzi fala narastaj&#x105;cych problem&#xF3;w i frustracji. Nie da si&#x119; ukry&#x107;, &#x17C;e rzeczy trudne wymagaj&#x105; nie tylko pe&#x142;nego zaanga&#x17C;owania, ale r&#xF3;wnie&#x17C; poch&#x142;aniaj&#x105;&#xA0;ogromn&#x105; ilo&#x15B;&#x107;&#xA0;czasu i uwagi. Nie chodzi jedynie o to, by i&#x15B;&#x107; do przodu, ale tak&#x17C;e, a mo&#x17C;e przede wszystkim o to, by napotkana trudno&#x15B;&#x107; czy niepowodzenie nie zgasi&#x142;y naszej motywacji.</p><p>Gdy napotykamy op&#xF3;r, pr&#x119;dzej czy p&#xF3;&#x17A;niej pojawia si&#x119; pytanie, kt&#xF3;re jest tematem przewodnim dzisiejszego wpisu: <em>Ile jeszcze?</em> </p><hr><p>Wielokrotnie spotyka&#x142;em si&#x119; z tym pytaniem prowadz&#x105;c zaj&#x119;cia z programowania.</p><p>Co warto podkre&#x15B;li&#x107;, sam czas trwania nauki wydaje si&#x119; nie by&#x107; najwa&#x17C;niejsz&#x105; kwesti&#x105;, lecz przyj&#x119;ta postawa, kt&#xF3;ra opiera si&#x119;&#xA0;na jak najszybszym osi&#x105;gni&#x119;ciu celu, a nie na samej drodze.</p><p>Stawiaj&#x105;c pytanie <em>ile jeszcze? </em>chcia&#x142;oby si&#x119; spojrze&#x107; na to wszystko z g&#xF3;ry tak, aby widzie&#x107; przed sob&#x105;&#xA0;nauk&#x119; rozbit&#x105; na poszczeg&#xF3;lne etapy. Chyba ka&#x17C;dy chcia&#x142;by znale&#x17A;&#x107; z&#x142;ot&#x105; metod&#x119; na osi&#x105;gni&#x119;cie zamierzonego celu w rozs&#x105;dnych ramach czasowych.</p><p>Niestety, w praktyce nie jest to ju&#x17C; takie &#x142;atwe. Ilekro&#x107; pr&#xF3;bowa&#x142;em uczy&#x107; si&#x119; nowych rzeczy patrz&#x105;c na nauk&#x119; z g&#xF3;ry, tylekro&#x107; ta nauka ko&#x144;czy&#x142;a si&#x119; fiaskiem.</p><p>Z biegiem lat, pytanie: <em>ile jeszcze?</em> nabra&#x142;o dla mnie innego znaczenia. Postrzegam je jako sygna&#x142; ostrzegawczy przed przyj&#x119;ciem niew&#x142;a&#x15B;ciwej postawy do nauki.</p><hr><p>W tym wpisie chcia&#x142;bym r&#xF3;wnie&#x17C; przedstawi&#x107; alternatywne podej&#x15B;cie, kt&#xF3;re u&#x142;atwia nauk&#x119; i pozwala na ni&#x105; spojrze&#x107; z innej perspektywy. &#xA0;To podej&#x15B;cie to:</p><h2 id="odnajd%C5%BA-rado%C5%9B%C4%87-i-zabaw%C4%99-w-programowaniu"> &#xA0; &#xA0; &#xA0;odnajd&#x17A; rado&#x15B;&#x107; i zabaw&#x119; w programowaniu</h2><p></p><p></p><p>Czy to &#x17C;art? Ale&#x17C;&#xA0;nie! B&#x119;d&#x105;c w sytuacji gdy nauka nie idzie zgodnie z planem, co jest wi&#x119;kszego do stracenia?</p><p>Zabawa na pierwszy rzut oka, wydaje si&#x119; zarazem taka dziecinna i niepowa&#x17C;na. Z wiekiem, nam doros&#x142;ym coraz trudniej przychodzi zaakceptowanie jej sprawczo&#x15B;ci, dlatego nie my&#x15B;l&#x105;c zbyt wiele, odrzucamy to podej&#x15B;cie. Warto jednak si&#x119; prze&#x142;ama&#x107;, da&#x107; sobie szans&#x119;, poniewa&#x17C; zmiana my&#x15B;lenia mo&#x17C;e przynie&#x15B;&#x107; wymierne korzy&#x15B;ci. Co z reszt&#x105; wida&#x107; w przypadku dzieci, kt&#xF3;re szybko i sprawnie przyswajaj&#x105; nowe rzeczy.</p><p>Nauka nie powinna stwarza&#x107; presji, nie trzeba&#xA0;wszystkiego wiedzie&#x107; ani by&#x107; w tym p&#x142;ynnym czy perfekcyjnym, szczeg&#xF3;lnie na samym pocz&#x105;tku naszej drogi. Zdecydowanie lepiej jest dzia&#x142;a&#x107; wolniej, eksplorowa&#x107; i wydobywa&#x107; z nauki to co zaciekawi i zachwyci.</p><p>Podczas nauki warto zastanowi&#x107; si&#x119;, czy jest jaki&#x15B; wycinek przerobionego materia&#x142;u, kt&#xF3;ry wydaje si&#x119; ciekawy, a zarazem przyst&#x119;pny. Je&#x15B;li tak to dobrym pomys&#x142;em b&#x119;dzie po&#x15B;wi&#x119;cenie dodatkowych godzin i energii na dodatkow&#x105; eksploracj&#x119;: to mo&#x17C;e to by&#x107; drobny program, eksperyment albo nietypowe u&#x17C;ycie. </p><p>Moim zdaniem, pod&#x105;&#x17C;anie za ciekawo&#x15B;ci&#x105; i wyobra&#x17A;ni&#x105; jest kluczem do efektywnej nauki, kt&#xF3;rej nieod&#x142;&#x105;cznym elementem powinna by&#x107; dobra zabawa.</p>]]></content:encoded></item><item><title><![CDATA[Warstwy - korzyści i koszty]]></title><description><![CDATA[Architektura warstwowa (eng. Layered Architecture) to popularny wzorzec przy tworzeniu oprogramowania, który opiera się na rozdzieleniu interfejsu użytkownika, logiki i persystencji danych na osobne warstwy.]]></description><link>https://blog.devpython.pl/warstwy-korzysci-i-koszty/</link><guid isPermaLink="false">674a2b4b434bc2017158d12e</guid><category><![CDATA[metody]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Tue, 26 Aug 2025 19:00:16 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/08/warstwyZaso-b-7.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/08/warstwyZaso-b-7.svg" alt="Warstwy - korzy&#x15B;ci i koszty"><p>Architektura warstwowa (eng. <em>Layered Architecture</em>) to popularny wzorzec przy tworzeniu oprogramowania, kt&#xF3;ry opiera si&#x119; na rozdzieleniu interfejsu u&#x17C;ytkownika, logiki i persystencji danych na osobne warstwy.</p><p>Mo&#x17C;liwo&#x15B;&#x107; podzielenia projektu na warstwy sprawia, &#x17C;e przetestowanie ka&#x17C;dej z osobna jest nie tylko prostsze, ale r&#xF3;wnie&#x17C; szybsze i co wa&#x17C;ne dok&#x142;adniejsze ni&#x17C; w przypadku testowania aplikacji jako ca&#x142;o&#x15B;&#x107;. </p><p>Je&#x15B;li testy w projekcie nie opieraj&#x105; si&#x119; na rozbiciu, wtedy nie wykorzystujemy pe&#x142;nego potencja&#x142;u jaki niesie to podej&#x15B;cie. To tak jak korzysta&#x107; z urz&#x105;dze&#x144; elektrycznych, bez pod&#x142;&#x105;czania ich do pr&#x105;du.</p><p>Jednak nie da si&#x119; ukry&#x107;, &#x17C;e podzia&#x142; na warstwy poci&#x105;ga za sob&#x105;&#xA0;r&#xF3;wnie&#x17C; pewne koszty.</p><p>Pierwszym z nich jest fakt, &#x17C;e biznesowe szczeg&#xF3;&#x142;y s&#x105; roz&#x142;o&#x17C;one na wszystkie warstwy. W praktyce oznacza to, &#x17C;e chc&#x105;c zrozumie&#x107; dowoln&#x105; funkcjonalno&#x15B;&#x107; w projekcie, konieczne jest przej&#x15B;cie przez ka&#x17C;d&#x105; z warstw z osobna.</p><p>Drugim kosztem s&#x105; mo&#x17C;liwe negatywne konsekwencje wprowadzania zmian w projekcie. W omawianym podej&#x15B;ciu, g&#xF3;rne warstwy s&#x105; zale&#x17C;ne od dolnych, a wi&#x119;c je&#x15B;li dokonamy zmiany w dolnej warstwie, to ta zmiana mo&#x17C;e spowodowa&#x107; kaskadowe wprowadzenie modyfikacji we wszystkich g&#xF3;rnych warstwach sko&#x144;czywszy na warstwie prezentacji.</p>]]></content:encoded></item><item><title><![CDATA[Kiedy warto logować]]></title><description><![CDATA[Bez wątpienia logowanie zdarzeń to istotna praktyka. Logi są nieocenioną pomocą, w sytuacji, gdy próbujemy zidentyfikować potencjalne problemy jakie wystąpiły w czasie działania aplikacji.]]></description><link>https://blog.devpython.pl/kiedy-warto-logowac/</link><guid isPermaLink="false">6844809eac24600169516fdc</guid><category><![CDATA[python]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Thu, 14 Aug 2025 20:06:52 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/07/papirusZaso-b-6.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/07/papirusZaso-b-6.svg" alt="Kiedy warto logowa&#x107;"><p>Bez w&#x105;tpienia logowanie zdarze&#x144; to istotna praktyka. Logi s&#x105; nieocenion&#x105; pomoc&#x105;, w sytuacji, gdy pr&#xF3;bujemy zidentyfikowa&#x107; potencjalne problemy jakie wyst&#x105;pi&#x142;y w czasie dzia&#x142;ania aplikacji.</p><p>Niestety, w praktyce logi logom nie s&#x105; r&#xF3;wne. Czasem mamy ich w nadmiarze, a czasem zbyt ma&#x142;o, aby znale&#x17A;&#x107; przyczyn&#x119;&#xA0;b&#x142;&#x119;du. Niekiedy logi s&#x105; zbyt precyzyjne, a niekiedy zbyt szcz&#x105;tkowe w informacje. To wci&#x105;&#x17C; otwarty temat.</p><p>W tej notce chcia&#x142;bym poruszy&#x107;&#xA0;kwesti&#x119; kiedy warto logowa&#x107;. </p><p>Czy na przyk&#x142;ad poni&#x17C;sza funkcja, kt&#xF3;ra odpowiada na pytanie, czy elementy s&#x105; posortowane, powinna cokolwiek logowa&#x107;? </p><pre><code>def ordered(xs):
    for i in len(range(xs) - 1):
        if xs[i] &gt; xs[i+1]:
            return False
    return True</code></pre><p>Moim zdaniem nie, poniewa&#x17C; ta funkcja jest oderwana od kontekstu u&#x17C;ycia i trudno oczekiwa&#x107;, by logowanie z jej wn&#x119;trza wnosi&#x142;o wi&#x119;ksz&#x105; warto&#x15B;&#x107;.</p><p>A teraz troch&#x119; trudniejszy przypadek. Mamy tu przyk&#x142;ad klasy, kt&#xF3;ra odzwierciedla pewne biznesowe wymagania. Czy w tej sytuacji warto w&#x142;&#x105;czy&#x107; logowanie?</p><pre><code>class Battery:

    def __init__(self, value, limit):
    	if limit &lt; 0:
            raise ValueError(&apos;Required positive limit&apos;)
        
        self._value = 0
        self._limit = limit
        
        self.charge(value)

    def charge(self, delta):
        if delta &lt; 0:
            raise ValueError(&apos;Required non-negative value&apos;)
        self._value = min(self._value + delta, self._limit)

    @property
    def percents(self):
        return self._value / self._limit * 100
</code></pre><p>Mimo biznesowej logiki nie zastosowa&#x142;bym tutaj &#x17C;adnego logowania, poniewa&#x17C; ta klasa jest oderwana od jej docelowego przypadku u&#x17C;ycia. Warto zwr&#xF3;ci&#x107; uwag&#x119;, &#x17C;e trudniejsze sytuacje, kt&#xF3;re mog&#x105; wp&#x142;yn&#x105;&#x107; negatywnie na jej stan, s&#x105;&#xA0;chronione poprzez wyj&#x105;tki.</p><h3 id="kiedy-warto-logowa%C4%87">Kiedy warto logowa&#x107;?</h3><p>Najlepszym miejscem do logowania s&#x105; procedury biznesowe, czyli funkcje na najwy&#x17C;szym poziomie, kt&#xF3;re odpowiadaj&#x105; za realizacj&#x119;&#xA0;biznesowych zada&#x144;. To w&#x142;a&#x15B;nie tam dysponujemy kontekstem na podstawie kt&#xF3;rego warto logowa&#x107; zdarzenia jakie maj&#x105; miejsce w aplikacji.</p>]]></content:encoded></item><item><title><![CDATA[Koszt abstrakcji]]></title><description><![CDATA[Największą korzyścią z abstrakcji jest to, że w przypadku potencjalnych zmian wymagań gry, utworzone funkcje/klasy pozostaną nienaruszone.]]></description><link>https://blog.devpython.pl/koszt-abstrakcji/</link><guid isPermaLink="false">6863ed5cac24600169519eaf</guid><category><![CDATA[metody]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Sun, 13 Jul 2025 16:22:02 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/07/sakiewkaZaso-b-4.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/07/sakiewkaZaso-b-4.svg" alt="Koszt abstrakcji"><p>Od czasu do czasu lubi&#x119; napisa&#x107; konsolow&#x105; gr&#x119;, kt&#xF3;ra stanowi dla mnie punkt wyj&#x15B;cia do postawienia r&#xF3;&#x17C;nych pyta&#x144; zwi&#x105;zanych z samym kodem, podzia&#x142;ami czy te&#x17C; technikami. W przeciwie&#x144;stwie do wi&#x119;kszych projekt&#xF3;w, praca nad drobnymi programami umo&#x17C;liwia uzyskanie szybszego feedbacku.</p><p>Podczas jednej z takich sesji, postawi&#x142;em sobie za cel oddzielenie zasadniczego kodu gry (czyli jej logiki) od <a href="https://blog.devpython.pl/abstrakcja/">rzeczy abstrakcyjnych</a>, kt&#xF3;re nie by&#x142;y bezpo&#x15B;rednio powi&#x105;zane ze szczeg&#xF3;&#x142;ami gry.</p><p>Zgodnie z tym za&#x142;o&#x17C;eniem, wydzieli&#x142;em nast&#x119;puj&#x105;ce funkcje i klasy o charakterze abstrakcyjnym:</p><pre><code>def matrix_find_value(matrix, x):
    for row in matrix:
        for value in row:
            if value == x:
                return True
    return False


def select(coll, indices):
    return [coll[i] for i in indices]


class ParserError(Exception):
    pass


def ask(parser, prompt=&apos;&gt; &apos;):
    while True:
        try:
            return parser(input(prompt))
        except ParserError as e:
            print(str(e) + &quot;, try again&quot;)


class OneOfNumberParser:

    def __init__(self, numbers):
        self._numbers = numbers

    def __call__(self, s):
        try:
            value = int(s)
        except ValueError:
            raise ValueError(&apos;Required number&apos;)

        if value not in self._numbers:
            raise ValueError(&apos;Not available number&apos;)

        return value
</code></pre><p>Najwi&#x119;ksz&#x105; korzy&#x15B;ci&#x105; tego kodu jest to, &#x17C;e w przypadku potencjalnych zmian wymaga&#x144; gry, utworzone funkcje/klasy pozostan&#x105; nienaruszone.</p><p>Sp&#xF3;jrzmy na przyk&#x142;ad na funkcj&#x119; <code>select</code>. Bez wzgl&#x119;du na charakter &#xA0;wprowadzonych zmian w grze, funkcja ta ca&#x142;y czas b&#x119;dzie odpowiada&#x107; za pobranie wielu warto&#x15B;ci z listy po indeksach.</p><p>Jedynie w przypadku wi&#x119;kszych zmian cz&#x119;&#x15B;&#x107; kodu mo&#x17C;e okaza&#x107; si&#x119;&#xA0;zb&#x119;dna. </p><p>W ostateczno&#x15B;ci, w przypadku wi&#x119;kszych zmian w logice gry, cz&#x119;&#x15B;&#x107; kodu mo&#x17C;e okaza&#x107; si&#x119; co najwy&#x17C;ej niepotrzebna. Istnieje mo&#x17C;liwo&#x15B;&#x107;, &#x17C;e funkcja <code>ask</code> b&#x119;dzie do usuni&#x119;cia, gdy docelowa gra zacznie dzia&#x142;a&#x107; w trybie graficznym.</p><p>Z drugiej strony mo&#x17C;na zada&#x107; sobie pytanie: czy <code>ask</code> i <code>OneOfNumberParser</code> s&#x105; lepsze od nast&#x119;puj&#x105;cej funkcji?</p><pre><code>def update(board, symbol):
    while True:
        try:
            value = int(input(&apos;&gt; &apos;))
        except ValueError:
            print(&apos;Required number&apos;)
            continue

        if value &lt; 1 or 9 &lt; value:
            print(&apos;Required is number from 1 to 9&apos;)
            continue

        index = value - 1

        if board[index] != &apos;_&apos;:
            print(&apos;The position is taken&apos;)
            continue
        
        break
  
    board[index] = symbol
        </code></pre><p>Ta funkcja chocia&#x17C; zawiera szczeg&#xF3;&#x142;y gry (wi&#x119;c niestety jest podatna na zmiany), to mimo to jest &#x142;atwiejsza w zrozumieniu. </p><p>Warto zastanowi&#x107; si&#x119; nad zasadno&#x15B;ci&#x105; tworzenia abstrakcji.</p><p>Z jednej strony abstrakcje pozwalaj&#x105; kontrolowa&#x107; z&#x142;o&#x17C;ono&#x15B;&#x107;, pozwalaj&#x105; ukry&#x107; wszelkie nieistotne szczeg&#xF3;&#x142;y, ale z drugiej strony u&#x17C;ycie abstrakcji takich jak <code>ask</code> czy <code>OneOfNumberParser</code> czyni&#x105;&#xA0;kod nieco trudniejszy w zrozumieniu.</p><p>Gdy nie mamy pewno&#x15B;ci to lepiej <a href="https://blog.devpython.pl/kto-pyta-nie-bladzi/">poczeka&#x107; z odpowiedzi&#x105;</a>, by da&#x107; sobie troch&#x119; wi&#x119;cej czasu i dokona&#x107;&#xA0;przemy&#x15B;lanego wyboru.</p><p>My&#x15B;l&#x119;, &#x17C;e poni&#x17C;szy cytat <a href="http://nathanmarz.com/blog/suffering-oriented-programming.html">z tej strony</a> &#x15B;wietnie nawi&#x105;zuje do tematu abstrakcji:</p><blockquote>Don&apos;t build technology unless <strong>you feel the pain of not having it</strong>. It applies to the big, architectural decisions as well as the smaller everyday programming decisions.<br><br>&#x2013; Nathan Marz</blockquote><p>Cytat z ksi&#x105;&#x17C;ki <a href="https://helion.pl/ksiazki/reguly-programowania-jak-pisac-lepszy-kod-chris-zimmerman,regpro.htm">Regu&#x142;y programowania. Jak pisa&#x107; lepszy kod</a> r&#xF3;wnie&#x17C; trafnie opisuje to zagadnienie:</p><blockquote>Skuteczne programowanie polega na op&#xF3;&#x17A;nianiu tego, co nieuniknione.<br><br>&#x2013; Chris Zimmerman</blockquote>]]></content:encoded></item><item><title><![CDATA[Jak prowadzę zajęcia z programowania?]]></title><description><![CDATA[W tym wpisie chciałbym przedstawić moje podejście dotyczące prowadzenia zajęć z programowania w języku Python.]]></description><link>https://blog.devpython.pl/jak-prowadze-zajecia-z-programowania/</link><guid isPermaLink="false">68506d97ac246001695189ad</guid><category><![CDATA[metody]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Tue, 08 Jul 2025 20:09:48 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/06/bruceleeZaso-b-22.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/06/bruceleeZaso-b-22.svg" alt="Jak prowadz&#x119; zaj&#x119;cia z programowania?"><p>W tym wpisie chcia&#x142;bym przedstawi&#x107; moje podej&#x15B;cie dotycz&#x105;ce prowadzenia zaj&#x119;&#x107; z programowania w j&#x119;zyku Python.</p><p>Pierwsz&#x105; rzecz&#x105;, kt&#xF3;r&#x105; kieruj&#x119; si&#x119; nauczaj&#x105;c programowania to nie narzucanie &#x17C;adnego stylu, poniewa&#x17C; mam &#x15B;wiadomo&#x15B;&#x107;, &#x17C;e mo&#x17C;e to skomplikowa&#x107; nauk&#x119;. Trudniej jest wtedy oceni&#x107; czy nauka programowania zmierza w dobrym kierunku, szczeg&#xF3;lnie, gdy poruszane problemy staj&#x105; si&#x119; coraz bardziej wymagaj&#x105;ce.</p><p>Drug&#x105; kwesti&#x105;, kt&#xF3;ra ma dla mnie znaczenie, to ograniczenie materia&#x142;u do tego co niezb&#x119;dne. Z jednej strony by nie przyt&#x142;acza&#x107;, a z drugiej, &#x17C;eby celowo ograniczy&#x107; r&#xF3;&#x17C;norodno&#x15B;&#x107; u&#x17C;ywanych konstrukcji. Im mniej dost&#x119;pnych opcji, tym &#x142;atwiej si&#x119; skupi&#x107; na rozwi&#x105;zaniu &#x107;wiczenia, a tym samym osi&#x105;gn&#x105;&#x107; lepszy rezultat z nauki. </p><p>Poni&#x17C;szy cytat idealnie opisuje to podej&#x15B;cie:</p><blockquote>Brak stylu jest stylem, brak ogranicze&#x144; jest ograniczeniem.<br>&#x2013; Bruce Lee</blockquote><p></p>]]></content:encoded></item><item><title><![CDATA[Spłaszczenie listy]]></title><description><![CDATA[Algorytm spłaszczenia zagnieżdżonych list z kilku perspektyw.]]></description><link>https://blog.devpython.pl/splaszczenie-listy/</link><guid isPermaLink="false">68625740ac24600169519e2f</guid><category><![CDATA[algorytmy]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Sat, 05 Jul 2025 14:44:27 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/07/flattenZaso-b-5.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/07/flattenZaso-b-5.svg" alt="Sp&#x142;aszczenie listy"><p>Algorytm dotycz&#x105;cy sp&#x142;aszczenia listy jest dla mnie szczeg&#xF3;lny, poniewa&#x17C; podczas mojej pierwszej rozmowy kwalifikacyjnej otrzyma&#x142;em zadanie zwi&#x105;zane w&#x142;a&#x15B;nie z tym tematem.</p><p>Wtedy rozwi&#x105;zanie tego algorytmu nie przysz&#x142;o mi &#x142;atwo, ale z biegiem czasu uda&#x142;o mi si&#x119; wypracowa&#x107; bardziej wyszukane rozwi&#x105;zania.</p><p>Celem zadania by&#x142;o przekszta&#x142;cenie listy:</p><pre><code>[10, [20, [25], 30], 50]</code></pre><p>tak, by nie by&#x142;o w niej zagnie&#x17C;d&#x17C;e&#x144;:</p><pre><code>[10, 20, 25, 30, 50]</code></pre><p><strong>Pr&#xF3;ba pierwsza:</strong></p><pre><code>results = []

def flatten(xs):
    for x in xs:
        if isinstance(x, list):
            flatten(x)
        else:
            results.append(x)
            
</code></pre><p>To by&#x142;a moja pierwsza pr&#xF3;ba, w kt&#xF3;rej skorzysta&#x142;em z globalnej <code>results</code>. Wad&#x105; tego podej&#x15B;cia jest fakt, &#x17C;e wynik ponownego wywo&#x142;ania funkcji flatten zapisywany jest w tej samej kolekcji, a zatem sp&#x142;aszczaj&#x105;c dwie odr&#x119;bne listy uzyskamy jeden wsp&#xF3;lny rezultat.</p><p><strong>Pr&#xF3;ba druga:</strong></p><pre><code>def flatten(xs, results):
    for x in xs:
        if isinstance(x, list):
            flatten(x, results)
        else:
            results.append(x)
</code></pre><p>To rozwi&#x105;zanie niweluje u&#x17C;ycie globalnej zmiennej. W tym przypadku funkcja wymaga przekazania listy, kt&#xF3;ra w ramach jej wywo&#x142;ania zostanie zmodyfikowana. W tej li&#x15B;cie b&#x119;d&#x105; zachowane wyniki. To na co warto zwr&#xF3;ci&#x107; uwag&#x119;&#xA0;to fakt, &#x17C;e funkcja ta nie zwraca wyniku, poniewa&#x17C; z zasady funkcja, kt&#xF3;ra modyfikuje argument, nie powinna zwraca&#x107; go z u&#x17C;yciem instrukcji return.<br><br>Poni&#x17C;szy kod rozszerza to u&#x17C;ycie:</p><pre><code>def flatten(xs):
    results = []
    _flatten(xs, results)
    return results
    

def _flatten(xs, results):
    for x in xs:
        if isinstance(x, list):
            _flatten(x, results)
        else:
            results.append(x)</code></pre><p>Funkcja flatten rozpoczyna wykonanie zadania poprzez stworzenie wynikowej pustej listy, by potem przekaza&#x107; j&#x105; do _flatten i na sam koniec zwr&#xF3;ci&#x107; wynik.</p><p><strong>Pr&#xF3;ba trzecia</strong></p><p>Poni&#x17C;sze rozwi&#x105;zanie opiera si&#x119; na instrukcji return, kt&#xF3;ra zwraca po&#x15B;rednie wyniki. Przyk&#x142;ad ten idealnie obrazuje jak z&#x142;o&#x17C;ony problem zostaje podzielony na mniejsze cz&#x119;&#x15B;ci. Warto zauwa&#x17C;y&#x107;, &#x17C;e ka&#x17C;dorazowe u&#x17C;ycie flatten zwraca sp&#x142;aszczon&#x105; list&#x119;, kt&#xF3;ra wymaga uj&#x119;cia w ko&#x144;cowym wyniku.</p><pre><code>def flatten(xs):
    results = []
    for x in xs:
        if isinstance(x, list):
            results.extend(flatten(x))
        else:
            results.append(x)
    return results</code></pre><p>Omawiany temat poruszy&#x142;em tak&#x17C;e w filmiku:</p><!--kg-card-begin: html--><iframe width="560" height="315" src="https://www.youtube.com/embed/WmTP7jGjPiQ?si=1-xBVwvOnKmeHBI6" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe><!--kg-card-end: html--><p><strong>Pr&#xF3;ba czwarta</strong></p><p>Wszystkie przedstawione wcze&#x15B;niej podej&#x15B;cia korzystaj&#x105; z rekurencji, kt&#xF3;ra wg mnie jest najczytelniejszym sposobem rozwi&#x105;zania tego problemu. Niestety poleganie na rekurencji nie zawsze jest dobrym pomys&#x142;em, poniewa&#x17C; opiera si&#x119; ona na stosie wywo&#x142;a&#x144;, kt&#xF3;ry w du&#x17C;ym stopniu ma ograniczon&#x105; pami&#x119;&#x107;.</p><p>Poni&#x17C;sze rozwi&#x105;zanie wykorzystuje jawny stos (w oparciu o list&#x119;).<br><br>Nale&#x17C;y tutaj pami&#x119;ta&#x107;, aby po przetworzeniu warto&#x15B;ci zwr&#xF3;ci&#x107;&#xA0;wynik w odwrotnej kolejno&#x15B;ci.</p><pre><code>def flatten(xs):
    results = []
    stack = [xs]
    while stack:
        x = stack.pop()
        if isinstance(x, list):
            stack.extend(x)
        else:
            results.append(x)
    return results[::-1]</code></pre><p>Zach&#x119;cam tak&#x17C;e do zapoznania si&#x119;&#xA0;z poni&#x17C;szym filmikiem, kt&#xF3;ry omawia ten temat.</p><!--kg-card-begin: html--><iframe width="560" height="315" src="https://www.youtube.com/embed/H0zjXcWtCx4?si=MW4vhJ00iixeNuHe" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Kto pyta, nie błądzi]]></title><description><![CDATA[Szukanie odpowiedzi na nurtujące nas pytania to najlepszy sposób na poszerzanie wiedzy.]]></description><link>https://blog.devpython.pl/kto-pyta-nie-bladzi/</link><guid isPermaLink="false">68531647ac24600169519243</guid><category><![CDATA[metody]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Sat, 28 Jun 2025 20:10:25 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/06/ktopytaZaso-b-21.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/06/ktopytaZaso-b-21.svg" alt="Kto pyta, nie b&#x142;&#x105;dzi"><p>Gdybym mia&#x142; wskaza&#x107; jedn&#x105; rzecz, kt&#xF3;ra w najwi&#x119;kszym stopniu u&#x142;atwi&#x142;a mi nauk&#x119; programowania, to by&#x142;oby to&#xA0;systematyczne zadawanie pyta&#x144; zwi&#x105;zanych z projektami nad kt&#xF3;rymi pracowa&#x142;em, jak r&#xF3;wnie&#x17C; z narz&#x119;dziami z kt&#xF3;rych regularnie korzysta&#x142;em. </p><p>Stawiaj&#x105;c sobie r&#xF3;&#x17C;ne pytania nie wymaga&#x142;em od siebie natychmiastowej i najlepszej odpowiedzi, wr&#x119;cz przeciwnie, si&#x142;a tego podej&#x15B;cia tkwi&#x142;a w regularnym wracaniu do tych pyta&#x144; i patrzeniu na problemy z r&#xF3;&#x17C;nych perspektyw. Dzi&#x119;ki tej metodzie, moje odpowiedzi z biegiem czasu dojrzewa&#x142;y i nabiera&#x142;y wi&#x119;kszej g&#x142;&#x119;bi.</p><p>Musz&#x119;&#xA0;przyzna&#x107;, &#x17C;e r&#xF3;wnie&#x17C; i dzi&#x15B; nie stroni&#x119; od zadawania sobie pyta&#x144;, bo przecie&#x17C;&#xA0;szukanie w&#x142;a&#x15B;ciwej odpowiedzi na nurtuj&#x105;ce zagadnienia, to najlepszy spos&#xF3;b na poszerzanie wiedzy.</p>]]></content:encoded></item><item><title><![CDATA[Funkcje i zgłaszanie błędu]]></title><description><![CDATA[Jedną z zasad jaką kieruję się w czasie pisania kodu jest dbanie o to, by zgłaszać błędy możliwie blisko źródła ich wystąpienia.]]></description><link>https://blog.devpython.pl/funkcje-i-zglaszanie-bledu/</link><guid isPermaLink="false">68447d22ac24600169516f94</guid><category><![CDATA[python]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Thu, 26 Jun 2025 20:32:12 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/06/errorZaso-b-20.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/06/errorZaso-b-20.svg" alt="Funkcje i zg&#x142;aszanie b&#x142;&#x119;du"><p>Jedn&#x105;&#xA0;z zasad jak&#x105; kieruj&#x119;&#xA0;si&#x119; w czasie pisania kodu jest dbanie o to, by zg&#x142;asza&#x107; b&#x142;&#x119;dy mo&#x17C;liwie blisko &#x17A;r&#xF3;d&#x142;a ich wyst&#x105;pienia. Temat wydaje si&#x119; trywialny, ale od zwyk&#x142;ej funkcji oczekuj&#x119;, &#x17C;e zrobi jedn&#x105; z dw&#xF3;ch poni&#x17C;szych rzeczy:</p><p>1) zrealizuje zadanie z sukcesem<br>2) albo zg&#x142;osi b&#x142;&#x105;d.</p><p>Funkcje, kt&#xF3;re nie zg&#x142;aszaj&#x105; b&#x142;&#x119;d&#xF3;w s&#x105; k&#x142;opotliwe, poniewa&#x17C; znalezienie przyczyny wyciszonego b&#x142;&#x119;du jest kosztowne.</p><p></p>]]></content:encoded></item><item><title><![CDATA[Nazewnictwo]]></title><description><![CDATA[Dobieranie trafnych nazw w kodzie to często wymagające zadanie.]]></description><link>https://blog.devpython.pl/nazewnictwo/</link><guid isPermaLink="false">6749fe84434bc2017158cec1</guid><category><![CDATA[python]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Tue, 24 Jun 2025 20:09:04 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/06/rzezbaZaso-b-1.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/06/rzezbaZaso-b-1.svg" alt="Nazewnictwo"><p>Dobieranie trafnych nazw w kodzie to cz&#x119;sto wymagaj&#x105;ce zadanie. My&#x15B;l&#x119;, &#x17C;e niemal ka&#x17C;dy programista zgodzi si&#x119; z tym stwierdzeniem.</p><p>Ten temat zosta&#x142; poruszony w publikacjach takich jak: <a href="https://helion.pl/ksiazki/czysty-kod-podrecznik-dobrego-programisty-robert-c-martin,czykvv.htm">Czysty kod. Podr&#x119;cznik dobrego programisty</a> czy <a href="https://helion.pl/ksiazki/kod-doskonaly-jak-tworzyc-oprogramowanie-pozbawione-bledow-wydanie-ii-steve-mcconnell,koddov.htm">Kod doskona&#x142;y. Jak tworzy&#x107; oprogramowanie pozbawione b&#x142;&#x119;d&#xF3;w</a> i na blogu <a href="https://stuartsierra.com/2016/01/09/how-to-name-clojure-functions">Digital Digressions by Stuart Sierra</a>. </p><p>W poni&#x17C;szej notce chcia&#x142;bym rozszerzy&#x107; kwesti&#x119; dobierania nazw o w&#x142;asne spostrze&#x17C;enia.</p><h3 id="1a-mo%C5%BCe-wyra%C5%BCenie-zamiast-nazwy">#1 - A mo&#x17C;e wyra&#x17C;enie zamiast nazwy?</h3><p>Pracuj&#x105;c z wysokopoziomowym j&#x119;zykiem, takim jak Python, zwykle oceniam czy wybrana cz&#x119;&#x15B;&#x107; kodu mo&#x17C;e zosta&#x107; uj&#x119;ta w formie wyra&#x17C;enia. <br>Drobne wyra&#x17C;enia cz&#x119;sto bowiem m&#xF3;wi&#x105; wi&#x119;cej ni&#x17C; sama nazwa i przede wszystkim nie wymagaj&#x105; dodatkowego skoku do tre&#x15B;ci funkcji, aby zrozumie&#x107; jej dzia&#x142;anie.</p><p>Wi&#x119;cej na temat wyra&#x17C;e&#x144; mo&#x17C;na znale&#x17A;&#x107; <a href="https://blog.devpython.pl/ekspresja-wyrazen-w-pythonie/">w tym wpisie</a>.</p><h3 id="2oce%C5%84-zakres-u%C5%BCycia">#2 - Oce&#x144; zakres u&#x17C;ycia</h3><p>Dobra nazwa, podobnie jak abstrakcja, wsp&#xF3;&#x142;gra z reszt&#x105; kodu dop&#xF3;ki ukrywa nieistotne szczeg&#xF3;&#x142;y. W takiej sytuacji &#x142;atwiej jest si&#x119; skupi&#x107; na tym co istotne. Tutaj pomocna wskaz&#xF3;wka: im mniejszy zakres u&#x17C;ycia wybranej zmiennej, tym &#x142;atwiej o dob&#xF3;r og&#xF3;lnej nazwy.</p><p>Temat abstrakcji zosta&#x142; om&#xF3;wiony <a href="https://blog.devpython.pl/abstrakcja/">tutaj</a>.</p><p><strong>Przyk&#x142;ad 1:</strong></p><!--kg-card-begin: markdown--><pre><code>def xmin(a, b):
    return a if a &lt; b else b
</code></pre>
<!--kg-card-end: markdown--><p>Funkcja <em>xmin</em> por&#xF3;wnuje dwie dowolne warto&#x15B;ci. Mog&#x105; to by&#x107; np. ceny, ale z perspektywy definicji funkcji nie ma to wi&#x119;kszego znaczenia, dlatego w tym przypadku nazwy <em>a</em> i <em>b </em>s&#x105; zupe&#x142;nie wystarczaj&#x105;ce.</p><p><strong>Przyk&#x142;ad 2:</strong></p><pre><code>def cheapest_product(products):
    return min(products, key=lambda p: p.price)
</code></pre><p>Funkcja <em>cheapest_products </em>przyjmuje r&#xF3;&#x17C;ne produkty, mog&#x105; to by&#x107; np. ksi&#x105;&#x17C;ki, filmy, ubrania i st&#x105;d nazwa <em>products</em> mimo &#x17C;e og&#xF3;lna, jest wystarczaj&#x105;ca. W definicji lambda u&#x17C;yto tak&#x17C;e nazw&#x119; <em>p</em>, poniewa&#x17C; jej zakres u&#x17C;ycia jest w&#x105;ski. </p><p><strong>Przyk&#x142;ad 3:</strong></p><p>Poni&#x17C;sza funkcja sprawdza czy komponenty na stronie s&#x105; posortowane rosn&#x105;co wzgl&#x119;dem osi Y.</p><!--kg-card-begin: markdown--><pre><code>def ordered(xs):
    for i in len(range(xs) - 1):
        if xs[i] &gt; xs[i+1]:
            return False
    return True
    
    
print(ordered([w.y for w in widgets]))
</code></pre>
<!--kg-card-end: markdown--><p>W kodzie zosta&#x142;a wyodr&#x119;bniona funkcja <em>ordered</em>, kt&#xF3;ra odpowiada na pytanie, czy wybrana sekwencja jest posortowana rosn&#x105;co. Warto zauwa&#x17C;y&#x107;, &#x17C;e ta funkcja jest oderwana od przypadku u&#x17C;ycia, nie pojawia si&#x119; tutaj nazwa widgetu, lecz tylko <em>xs</em>, kt&#xF3;ra oznacza sekwencj&#x119; warto&#x15B;ci.</p><p><strong>Podsumowanie</strong></p><p>Wszystkie trzy przyk&#x142;ady pozwalaj&#x105; na stosowanie kr&#xF3;tkich i og&#xF3;lnych nazw, poniewa&#x17C; funkcje s&#x105; oderwane od ich kontekstu.</p>]]></content:encoded></item><item><title><![CDATA[Kiedy przydaje się generator?]]></title><description><![CDATA[Generator może być użyteczny, nawet jeśli celem nie jest przetwarzanie dużych zbiorów danych.]]></description><link>https://blog.devpython.pl/kiedy-przydaje-sie-generator/</link><guid isPermaLink="false">6730e169434bc2017158af87</guid><category><![CDATA[python]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Wed, 18 Jun 2025 18:09:00 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/06/robot2Zaso-b-6.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/06/robot2Zaso-b-6.svg" alt="Kiedy przydaje si&#x119; generator?"><p>Generatory przydaj&#x105; si&#x119; do pracy z du&#x17C;ymi zbiorami danych, kt&#xF3;re nie mieszcz&#x105; si&#x119; w pami&#x119;ci komputera. Natomiast sam musz&#x119; przyzna&#x107;, &#x17C;e w mojej pracy zawodowej nie przetwarza&#x142;em tak du&#x17C;ych zbior&#xF3;w danych bezpo&#x15B;rednio w Pythonie, aby mie&#x107;&#xA0;okazj&#x119; wykorzysta&#x107; generator.</p><p>Z czasem zrozumia&#x142;em, &#x17C;e stosowanie generatora tak naprawd&#x119; nie wymaga przetwarzania danych na du&#x17C;&#x105; skal&#x119;. O tym w&#x142;a&#x15B;nie w dzisiejszym wpisie.</p><p>Na samym pocz&#x105;tku warto por&#xF3;wna&#x107; zapis funkcji i generatora:</p><!--kg-card-begin: html--><pre>
def select_odd_numbers(xs):
    results = []
    for x in xs:
        if x % 2 == 1:
            results.append(x)
    return results
</pre><!--kg-card-end: html--><!--kg-card-begin: html--><pre>
def select_odd_numbers(xs):
    for x in xs:
        if x % 2 == 1:
            yield x
</pre><!--kg-card-end: html--><p>Pierwsz&#x105; znacz&#x105;c&#x105; r&#xF3;&#x17C;nic&#x105; w tym przyk&#x142;adzie jest fakt, &#x17C;e wywo&#x142;anie funkcji poci&#x105;ga za sob&#x105; wykonanie ca&#x142;ego kodu. Nie mo&#x17C;na przerwa&#x107; wykonania funkcji od zewn&#x105;trz, poniewa&#x17C; jest ona niepodzielna.</p><p>Dodatkowo warto zwr&#xF3;ci&#x107; uwag&#x119;, &#x17C;e powy&#x17C;sza funkcja na czas wykonania, musi buforowa&#x107; wynik swojej pracy w pomocniczej li&#x15B;cie.</p><p>Przypadek generatora jest odmienny, poniewa&#x17C; jego praca jest podzielona na etapy. Przej&#x15B;cie przez nie wymaga korzystania z funkcji <code>next</code>. To sprawia, &#x17C;e generator nie musi ani buforowa&#x107; wynik&#xF3;w ani realizowa&#x107; w ca&#x142;o&#x15B;ci swojej pracy.</p><p>Znaj&#x105;c zasadnicz&#x105; r&#xF3;&#x17C;nic&#x119; mi&#x119;dzy funkcj&#x105;, a generatorem mo&#x17C;emy &#x142;atwiej przeanalizowa&#x107; poni&#x17C;szy przyk&#x142;ad:</p><!--kg-card-begin: html--><pre>
for x in select_odd_numbers(numbers):
    if x == 13:
        break
</pre><!--kg-card-end: html--><p>W przypadku, gdy <code>select_odd_numbers</code> jest generatorem, <code>break</code> powoduje nie tylko przerwanie p&#x119;tli, ale r&#xF3;wnie&#x17C; ko&#x144;czy prac&#x119; generatora. Liczba 13 jest ostatni&#x105; liczb&#x105; jak&#x105; wyprodukuje generator.</p><p>U&#x17C;ycie &#xA0;<code>select_odd_numbers</code> jako funkcji w tej sytuacji nie jest zasadne, poniewa&#x17C; przed wykonaniem p&#x119;tli, kod funkcji musi uko&#x144;czy&#x107; si&#x119; wcze&#x15B;niej, co w wielu przypadkach mo&#x17C;e okaza&#x107; si&#x119; niewydajne.</p><p><strong>Generatory z racji leniwego wykonania, mog&#x105; wyodr&#x119;bni&#x107; cz&#x119;&#x15B;&#x107;&#xA0;dzia&#x142;a&#x144; z p&#x119;tli, bez uszczerbku na jej wydajno&#x15B;ci.</strong></p>]]></content:encoded></item><item><title><![CDATA[Czytelność kodu]]></title><description><![CDATA[Pierwszy raz o pojęciu czysty kod dowiedziałem się przeglądając w księgarni książkę Czysty kod. Podręcznik dobrego programisty. ]]></description><link>https://blog.devpython.pl/czytelnosc-kodu/</link><guid isPermaLink="false">677810e9434bc2017158dbcf</guid><category><![CDATA[metody]]></category><dc:creator><![CDATA[Paweł Kozak]]></dc:creator><pubDate>Sun, 15 Jun 2025 10:28:00 GMT</pubDate><media:content url="https://blog.devpython.pl/content/images/2025/06/czystykodZaso-b-17.svg" medium="image"/><content:encoded><![CDATA[<img src="https://blog.devpython.pl/content/images/2025/06/czystykodZaso-b-17.svg" alt="Czytelno&#x15B;&#x107; kodu"><p>Pierwszy raz o poj&#x119;ciu czysty kod dowiedzia&#x142;em si&#x119; przegl&#x105;daj&#x105;c w ksi&#x119;garni ksi&#x105;&#x17C;k&#x119; <a href="https://helion.pl/ksiazki/czysty-kod-podrecznik-dobrego-programisty-robert-c-martin,czykvv.htm">Czysty kod. Podr&#x119;cznik dobrego programisty</a>. Ksi&#x105;&#x17C;ka ta zrobi&#x142;a na mnie wtedy pozytywne wra&#x17C;enie, poniewa&#x17C; by&#x142;a ona inna od wszystkich jakie dotychczas zna&#x142;em. <br>To co wyr&#xF3;&#x17C;nia&#x142;o t&#x105; pozycj&#x119;, to poruszone w niej zagadnienia zwi&#x105;zane z jako&#x15B;ci&#x105; oprogramowania, a tak&#x17C;e cenne wskaz&#xF3;wki dotycz&#x105;ce samego kodu.</p><p>Najbardziej zapadaj&#x105;c&#x105; w pami&#x119;&#x107; wskaz&#xF3;wk&#x105; z ksi&#x105;&#x17C;ki by&#x142; poni&#x17C;szy cytat:</p><blockquote>Funkcje powinny wykonywa&#x107; jedn&#x105; operacj&#x119;. Powinny robi&#x107; to dobrze. Powinny robi&#x107; tylko to.<br>&#x2013; Czysty Kod, Robert C. Martin</blockquote><p>Pocz&#x105;tkowo nie zwraca&#x142;em uwagi na narastaj&#x105;c&#x105; z&#x142;o&#x17C;ono&#x15B;&#x107;, poniewa&#x17C; w wi&#x119;kszo&#x15B;ci przypadk&#xF3;w, kod kt&#xF3;ry wydzieli&#x142;em mog&#x142;em &#x142;atwiej i dok&#x142;adniej przetestowa&#x107;. By&#x142;a to zas&#x142;u&#x17C;ona nagroda za w&#x142;o&#x17C;ony trud.</p><p>Z perspektywy czasu widz&#x119;, &#x17C;e k&#x142;ad&#x142;em zbyt du&#x17C;y nacisk na rozbijanie kodu, pod&#x105;&#x17C;aj&#x105;c &quot;&#x15B;lepo&quot; za za&#x142;o&#x17C;eniami jakie sta&#x142;y za czystym kodem. </p><p>Niestety, to co mo&#x17C;e wymkn&#x105;&#x107; si&#x119;&#xA0;spod kontroli pisz&#x105;c czysty kod, to paradoksalnie utrata czytelno&#x15B;ci kodu. Porozbijany kod jest niemo&#x17C;liwy w czytaniu linia po linii jak opowiadanie. W pogoni za szczeg&#xF3;&#x142;ami trzeba skaka&#x107; po plikach i &#x142;&#x105;czy&#x107; wiele fakt&#xF3;w, aby zrozumie&#x107; to za co odpowiada aktualny wybrany fragment kodu.</p><p>Kto&#x15B; mo&#x17C;e powiedzie&#x107;: <em>Po to dziel&#x119; kod na funkcje/klasy, aby nie my&#x15B;le&#x107; o szczeg&#xF3;&#x142;ach</em>.</p><p>Problem w tym, &#x17C;e w przypadku biznesowego kodu, biznesowe szczeg&#xF3;&#x142;y s&#x105;&#xA0;istotne i pr&#xF3;ba ich ukrywania zwykle zawodzi, wywo&#x142;uj&#x105;c efekt odwrotny do zamierzonego. Innymi s&#x142;owy, tworzenie <a href="https://blog.devpython.pl/abstrakcja/">abstrakcji </a> z biznesowych szczeg&#xF3;&#x142;&#xF3;w mo&#x17C;e okaza&#x107; si&#x119; nie do ko&#x144;ca trafionym pomys&#x142;em.</p><p>Czyli jak wida&#x107; za&#x142;o&#x17C;enia czystego kodu, nie zawsze si&#x119; sprawdzaj&#x105;, poniewa&#x17C; rozbijanie kodu mo&#x17C;e wp&#x142;yn&#x105;&#x107; negatywnie na jego czytelno&#x15B;&#x107;.</p><p>Warto dopuszcza&#x107; do powt&#xF3;rze&#x144;, a w rezultacie <a href="https://blog.devpython.pl/dry/">zwleka&#x107; z DRY</a>. Dzi&#x119;ki temu mo&#x17C;emy zredukowa&#x107; liczb&#x119; mo&#x17C;liwych skok&#xF3;w w czytaniu, do tych niezb&#x119;dnych w zrozumieniu rozpatrywanego kodu.</p><p>Zar&#xF3;wno podej&#x15B;cie z czystym kodem, jak i podej&#x15B;cie z tolerowaniem powt&#xF3;rze&#x144; ma na celu u&#x142;atwienie pracy z oprogramowaniem. To pierwsze u&#x142;atwia modyfikowanie, poniewa&#x17C; zmian&#x119;&#xA0;wprowadza si&#x119; tylko w jednym miejscu, a powsta&#x142;y efekt ma szeroki zasi&#x105;g. Drugie podej&#x15B;cie umo&#x17C;liwia skupienie ca&#x142;ej uwagi na czytaniu i analizie kodu dost&#x119;pnym w jednym miejscu. </p><p>W praktyce korzystam z obu podej&#x15B;&#x107;. Pisz&#x105;c kod preferuj&#x119;&#xA0;wydziela&#x107; te cz&#x119;&#x15B;ci kodu, kt&#xF3;re maj&#x105; marginaln&#x105; rol&#x119; i nie s&#x105; zwi&#x105;zane bezpo&#x15B;rednio z biznesem. <br>Natomiast cz&#x119;&#x15B;&#x107; biznesow&#x105; pisz&#x119; bez nacisku na wyodr&#x119;bnianie. Wychodz&#x119; z za&#x142;o&#x17C;enia, &#x17C;e biznesowa logika do pewnego stopnia mo&#x17C;e i ma prawo si&#x119; powtarza&#x107;.</p>]]></content:encoded></item></channel></rss>