PPy3/DobrePraktyki

Z Brain-wiki
Wersja z dnia 14:06, 7 mar 2019 autorstwa RobertJB (dyskusja | edycje) (pierwsza wersja)
(różn.) ← poprzednia wersja | przejdź do aktualnej wersji (różn.) | następna wersja → (różn.)

Zasady ,,Dobrej praktyki"

Pod tym hasłem rozumiem zasady, które nie są regułami języka programowania, a program je naruszający może działać poprawnie a nawet całkiem nieźle. Nie są one też podatne na w pełni precyzyjne sformułowanie — a więc są to może bardziej wytyczne niż zasady. Nie należy ich też traktować jako obowiązujących w każdej sytuacji bez wyjątku; proponowałbym raczej przyjąć, że domyślnie się trzymamy tych zasad, a odstępujemy od nich raczej wyjątkowo i to wtedy, gdy takie odstępstwo ma wyraźne uzasadnienie.

Powodów dla przyjęcia takich zasad jest parę:

  • pisząc program należy liczyć się z tym, że powinien on nie tylko poprawnie być wykonywany przez komputer (tj. interpreter), ale powinien również nadawać się do czytania przez ludzi — którzy mogą potrzebować go poprawić, rozszerzyć, przystosować do własnych celów, lub wręcz będą chcieli się czegoś z niego nauczyć;
  • programowanie jest prawie zawsze częścią pracy zespołu, w którym uczestniczą zarówno osoby programujące, jak i wypełniające inne zadania. Dobrze jest przestrzegać zasad, które ułatwiają współpracę w ramach zespołu;
  • nawet jeżeli tworzymy program samodzielnie i wyłącznie na własny (i może nam się wydawać, że jednorazowy) użytek, to nieraz się zdarzy że będziemy chcieli go użyć ponownie w przyszłości, i być może jakoś poprawić lub zmodyfikować. Można to uważać za szczególny przypadek pracy zespołowej ;) Ja, piszący pewien kod dzisiaj, i ja po dwóch miesiącach, to w zasadzie różne osoby, i łatwo dochodzi do sytuacji, w której ta druga kompletnie nie rozumie co ta pierwsza miała na myśli pisząc dany kod...

Zgodność ze specyfikacją

Piszemy program, który wykonuje to zadanie, które zostało postawione — a nie jakieś zbliżone, choćby nam się wydawało, że tak będzie lepiej. Jeżeli sformułowanie zadania zawiera konkretne nazwy funkcji, modułów itp., konkretne sygnatury — tzn. że funkcja przyjmuje takie a nie inne argumenty, i zwraca taką a nie inną wartość — to tego się trzymamy. Jeśli uważamy, że specyfikacja nie jest dobra i należałoby ją poprawić, to dyskutujemy to ze współpracownikami. Oczywiście nie dotyczy to kontekstu kolokwium czy sprawdzianu ;) Wówczas jeśli mamy jakiekolwiek wątpliwości co do postawionego zadania, to pytamy i to od razu o wyjaśnienie czy też ujednoznacznienie.

Szczególny przypadek: gdy program ma ustalać sposób swojego działania na podstawie opcji i/lub argumentów z linii poleceń, to nie zastępujemy tego zapytaniem interaktywnym (odwołaniem do input())! I vice versa. W 90% przypadków (liczba z sufitu) to pierwsze rozwiązanie jest właściwsze, ale najważniejsze jest to, na co się umówiliśmy z zespołem.

Sensowne nazwy

Niektórzy twierdzą, że nadawanie nazw obiektom występującym w kodzie programu jest najtrudniejszym zadaniem programisty. Być może to przesada, ale złe nazewnictwo może być dużym utrudnieniem w zrozumieniu kodu programu (przez człowieka, nie przez maszynę). Przez złe nazewnictwo mam na myśli nazwy nic nie znaczące, lub wręcz wprowadzające w błąd. To, jakie są zasady właściwego nazewnictwa jest i pewnie zawsze będzie sporne; natomiast łatwo poznać, kiedy nazewnictwo jest ewidentnie złe.

Właściwy podział zadań

Typowy program (taki malutki; jest wiele bardziej złożonych) ma do wykonania zazwyczaj trzy zadania:

  • pozyskać dane wejściowe
  • dokonać ich przetworzenia, lub wyliczyć na ich podstawie jakiś wynik
  • przekazać ten wynik dalej, np. poinformować o nim użytkownika, lub zapisać do pliku.

Z wyjątkiem zupełnie trywialnych programów, mieszczących się w nie więcej niż kilku linijkach kodu, zadania te nie powinny być wykonywane łącznie w jednym bloku. Zadanie środkowe, jeżeli jest w nim cokolwiek nietrywialnego, powinno być wyodrębnione np. w funkcję (lub kilka), której zupełnie nie obchodzi skąd się biorą dane wejściowe (jej argumenty), ani co się stanie z wynikiem (wartością zwracaną). Jej zadaniem powinno być tylko i wyłącznie zrealizowanie algorytmu przetwarzania danych czy obliczenia wyniku. Sprzyja to czytelności kodu, ułatwia jego testowanie i ewentualne wielokrotne wykorzystanie. W szczególności, blok instrukcji zajmujący się pozyskaniem danych wejściowych i ,,zagospodarowaniem" wyniki powinien być opakowany w instrukcję warunkową if __name__ == '__main__':, gdyż instrukcje tam zawarte są niestosowne w przypadku, gdy chcielibyśmy funkcjonalność ,,algorytmiczną" kodu tego programu importować do wykorzystania w innym programie. Inaczej mówiąc, piszmy programy tak, aby dało się je wykorzystać bądź jako bezpośrednio wykonywalne, bądź jako moduły.

Ewidentnym gwałtem na tej zasadzie jest np.:

  • gdy w kodzie funkcji przetwarzającej dane jest ,,zaszyta" nazwa pliku, z którego będą pobrane dane wejściowe
  • gdy funkcja realizująca algorytm wypisuje jego wynik wywołując print(), zamiast go zwrócić instrukcją return
  • gdy funkcja realizująca algorytm zawiera odwołania do input().

Właściwie w ,,algorytmicznej" części kodu nie powinno być w ogóle odwołań do input() ani do print(), ewentualnie te drugie mogą się pojawić na etapie testowania lub dla wyświetlania wskaźników postępu jeśli wykonanie programu może trwać długo.

CDN


poprzednie | strona główna | od początku ;)


--RobertJB (dyskusja) 15:06, 7 mar 2019 (CET)