Dane oryginału
Copyright © 2019 by Julien Danjou. Title of English-language original: Serious Python. Black- -Belt Advice on Deployment, Scalability, Testing, and More, ISBN 978-1-59327-878-6, published by No Starch Press. Polish-language edition copyright © 2019 by Polish Scientifi c Publishers PWN Wydawnictwo Naukowe PWN Spółka Akcyjna. All rights reserved.

Przekład

Natalia Chounlamany-Turalska na zlecenie WITKOM Witold Sikorski

Projekt okładki polskiego wydania

Hubert Zacharski na podstawie oryginału

Wydawca

Edyta Kawala

Redaktor prowadzący

Jolanta Kowalczuk

Redaktor

Małgorzata Dąbkowska-Kowalik

Koordynator produkcji

Anna Bączkowska

Zastrzeżonych nazw fi rm i produktów użyto w książce wyłącznie w celu identyfi kacji.

Skład wersji elektronicznej na zlecenie Wydawnictwa Naukowego PWN

Monika Lipiec /Woblink

Copyright © for the Polish edition by Wydawnictwo Naukowe PWN SA

Warszawa 2019

ISBN 978-83-01-20641-3

eBook został przygotowany na podstawie wydania papierowego z 2019 r., (wyd. I)

Warszawa 2019

Wydawnictwo Naukowe PWN SA

02-460 Warszawa, ul. Gottlieba Daimlera 2

tel. 22 69 54 321, faks 22 69 54 288

infolinia 801 33 33 88

e-mail: pwn@pwn.com.pl; reklama@pwn.pl

www.pwn.pl

O autorze

Julien Danjou już od niemal dwudziestu lat współtworzy darmowe programy i od dwunastu lat zajmuje się tworzeniem oprogramowania w języku Python. Aktualnie pracuje na stanowisku kierownika zespołu projektowego dla platformy chmury OpenStack, która jest obecnie największą bazą kodu open source Pythona, zawierającą 2,5 miliona jego wierszy. Zanim zajął się budowaniem chmur, Julien utworzył rewelacyjnego menedżera okien i współuczestniczył w takich projektach jak Debian czy GNU Emacs.

O recenzencie technicznym

Mike Driscoll programuje w Pythonie od ponad dekady. Od wielu lat pisze o Pythonie na swoim blogu The Mouse vs. The Python. Mike jest autorem szeregu książek o Pythonie, takich jak Python 101, Python Interviews oraz ReportLab: PDF Processing with Python. Można go znaleźć na Twitterze lub GitHubie pod nazwą @driscollis.

Spis treści

Podziękowania

Wprowadzenie

Kto i dlaczego powinien przeczytać tę książkę

O tej książce

1. Rozpoczynanie projektu

Wersje Pythona

Układ projektu

Co robić

Czego nie robić

Numerowanie wersji

Style programowania i automatyczne sprawdzanie

Narzędzia do wykrywania błędów stylu

Narzędzia do wykrywania błędów programistycznych

Joshua Harlow o Pythonie

2. Moduły, biblioteki i platformy

System importowania

Moduł sys

Ścieżki importowania

Niestandardowe importery

Obiekty wyszukujące z listy sys.meta_path

Przydatne standardowe biblioteki

Zewnętrzne biblioteki

Lista wymagań, jakie powinna spełniać bezpieczna biblioteka zewnętrzna

Chronienie kodu przy użyciu opakowania API

Instalacja pakietu: dodatkowe możliwości narzędzia pip

Stosowanie i wybieranie platform

Doug Hellmann, Python Core Developer, o bibliotekach Pythona

3. Dokumentacja i najlepsze praktyki

Dokumentowanie przy użyciu Sphinksa

Rozpoczęcie pracy ze Sphinksem i reST

Moduły Sphinksa

Pisanie rozszerzeń Sphinksa

Zarządzanie zmianami interfejsów API

Numerowanie wersji API

Dokumentowanie zmian API

Oznaczanie przestarzałych funkcji przy użyciu modułu warnings

Podsumowanie

Christophe de Vienne o rozwijaniu interfejsów API

4. Obsługa znaczników czasu i stref czasowych

Problem brakujących stref czasowych

Budowanie domyślnych obiektów datetime

Rozpoznawanie stref czasowych przy użyciu dateutil

Serializowanie obiektów datetime rozpoznających strefę czasową

Rozwiązywanie problemu niejednoznacznych godzin

Podsumowanie

5. Dystrybucja oprogramowania

Krótka historia plików setup.py

Zarządzanie pakietami przy użyciu setup.cfg

Standardowy format dystrybucji wheel

Dzielenie się swoją pracą ze światem

Punkty wejścia

Wizualizowanie punktów wejścia

Wykorzystywanie skryptów konsoli

Wykorzystywanie wtyczek i sterowników

Podsumowanie

Nick Coghlan o zarządzaniu pakietami

6. Testy jednostkowe

Podstawy testowania

Proste testy

Pomijanie testów

Uruchamianie wybranych testów

Równoległe uruchamianie testów

Tworzenie obiektów wykorzystywanych w testach przy użyciu fikstur

Uruchamianie scenariuszy testowych

Kontrolowane testy bazujące na atrapach obiektów

Odkrywanie nieprzetestowanego kodu przy użyciu coverage

Środowiska wirtualne

Konfigurowanie środowiska wirtualnego

Stosowanie virtualenv wraz z narzędziem tox

Odtwarzanie środowiska

Stosowanie różnych wersji Pythona

Integrowanie innych testów

Zasady dotyczące testowania

Robert Collins o testowaniu

7. Metody i dekoratory

Dekoratory i kiedy ich używać

Tworzenie dekoratorów

Pisanie dekoratorów

Stosowanie wielu dekoratorów

Pisanie dekoratorów klas

W jaki sposób działają metody w Pythonie

Metody statyczne

Metody klasy

Metody abstrakcyjne

Mieszanie metod statycznych, klasy i abstrakcyjnych

Umieszczanie implementacji w metodach abstrakcyjnych

Prawda o metodzie super

Podsumowanie

8. Programowanie funkcyjne

Tworzenie czystych funkcji

Generatory

Tworzenie generatora

Zwracanie i przekazywanie wartości przy użyciu instrukcji yield

Inspekcje generatorów

Wyrażenia listowe

Funkcjonowanie funkcji funkcyjnych

Stosowanie funkcji na elementach przy użyciu funkcji map()

Filtrowanie list przy użyciu funkcji filter()

Pobieranie indeksów przy użyciu funkcji enumerate()

Sortowanie listy przy użyciu funkcji sorted()

Wyszukiwanie przy użyciu any() i all() elementów spełniających warunki

Łączenie list przy użyciu funkcji zip()

Rozwiązania typowych problemów

Przydatne funkcje itertools

Podsumowanie

9. Drzewo AST, Hy i atrybuty jak w języku Lisp

Analizowanie drzewa AST

Pisanie programu przy użyciu AST

Obiekty AST

Przechodzenie drzewa AST

Rozszerzanie narzędzia flake8 o analizy AST

Pisanie klasy

Pomijanie nieistotnego kodu

Sprawdzanie, czy istnieje odpowiedni dekorator

Poszukiwanie argumentu self

Szybkie wprowadzenie do Hy

Podsumowanie

Paul Tagliamonte o AST i Hy

10. Wydajność i optymalizacje

Struktury danych

Zrozumienie działania programu przez profilowanie

cProfile

Dezasemblowanie przy użyciu modułu dis

Efektywne definiowanie funkcji

Listy uporządkowane i bisect

namedtuple i __slots__

Memoizacja

Szybszy Python dzięki PyPy

Eliminacja kopiowania przy użyciu protokołu bufora

Podsumowanie

Victor Stinner o optymalizacji

11. Skalowalność i architektura

Wielowątkowość w Pythonie i jej ograniczenia

Przetwarzanie wieloprocesowe kontra wielowątkowe

Architektura sterowana zdarzeniami

Inne opcje i asyncio

Architektura zorientowana na usługi

Komunikacja między procesami przy użyciu ZeroMQ

Podsumowanie

12. Zarządzanie relacyjnymi bazami danych

Rozwiązania RDBMS, ORM i kiedy ich używać

Wewnętrzne bazy danych

Strumieniowe przesyłanie danych przy użyciu Flask i PostgreSQL

Pisanie aplikacji do strumieniowego przesyłania danych

Budowanie aplikacji

Dimitri Fontaine on Databases

13. Pisz mniej, programuj więcej

Użycie modułu six do wspierania wersji Python 2 i 3

Ciągi a unicode

Rozwiązywanie problemu przeniesienia modułów Pythona

Moduł modernize

Wykorzystywanie Pythona jak języka Lisp do tworzenia jednego dyspozytora

Tworzenie metod generycznych w języku Lisp

Metody generyczne w Pythonie

Menedżery kontekstu

Redukowanie szablonowego kodu przy użyciu attr

Podsumowanie

Aktualizacje

Przypisy

Podziękowania

Napisanie tej pierwszej książki wymagało ogromnego wysiłku. Z perspektywy czasu widzę, że nie miałem pojęcia, jak zwariowana będzie ta podróż i jak dużo przyniesie mi satysfakcji.

Jak to mówią, gdy chcesz iść szybko, idź sam, ale jeśli chcesz dojść daleko, to weź kogoś ze sobą. To czwarta edycja mojej książki i jej powstanie nie byłoby możliwe bez pomocy innych. Jest to wynik pracy zespołowej i chciałbym podziękować wszystkim, którzy wzięli w niej udział.

Większość osób bez namysłu zgodziła się udzielić mi wywiadu, obdarzając mnie zaufaniem oraz poświęciła swój czas, znacząco podnosząc wartość edukacyjną tej książki. Dziękuję Dougowi Hellmannowi za doskonałe porady dotyczące budowania bibliotek, Joshua Harlowowi za poczucie humoru i wiedzę o rozproszonych systemach, Christophowi de Vienne za doświadczenie w projektowaniu platform programistycznych, Victorowi Stinnerowi za niesamowitą wiedzę o CPythonie, Dimitrowi Fontaine za mądrość w dziedzinie baz danych, Robertowi Collinsowi za eksperymentowanie na testach, Nickowi Coghlanowi za pracę nad poprawą kondycji języka Python oraz Paulowi Tagliamonte za wspaniały hakerski zapał.

Dziękuję zespołowi No Starch za wspólną pracę nad wyniesieniem tej książki na całkiem nowy poziom, w szczególności dla Liz Chadwick za umiejętności redaktorskie, Laurel Chun za utrzymywanie mnie na właściwej drodze i Mikowi Driscollowi za wgląd techniczny.

Jestem wdzięczny społecznościom rozwijającym darmowe oprogramowanie, które dzieląc się swoją wiedzą, pomogły mi w rozwoju – w szczególności społeczności Pythona, która zawsze była otwarta i entuzjastyczna.

Wprowadzenie

Skoro sięgnąłeś po tę książkę, prawdopo dobnie już od jakiegoś czasu programujesz  w języku Python. Być może poznałeś go, korzystając z samouczków, dociekliwe analizując istniejące programy lub rozpocząłeś naukę od podstaw. Tak czy owak, opracowałeś swój sposób nauki. Ja poznawałem Pythona w taki sam sposób, dopóki 10 lat temu nie zacząłem pracy nad dużym projektem open source.

Po napisaniu pierwszego programu można łatwo odnieść wrażenie, że zrozumiało się Pythona. Język jest prosty w użyciu. Ale jego opanowanie i pełne zrozumienie zalet i wad wymagają lat praktyki.

Gdy zacząłem programowanie w Pythonie, budowałem swoje pierwsze biblioteki i aplikacje na skalę „domowych projektów”. Sytuacja zmieniła się, odkąd zacząłem wraz z setkami programistów pracować nad oprogramowaniem, z którego korzystają tysiące użytkowników. Na przykład platforma OpenStack – projekt, w którym współuczestniczę – składa się z 9 milionów wierszy kodu Pythona, który musi być zwięzły, efektywny i skalowalny, aby dostosowywać chmurę obliczeniową do potrzeb jej użytkowników. W projekcie o tak dużym rozmiarze, takie zadania jak testowanie czy dokumentowanie zdecydowanie wymagają automatyzacji, w przeciwnym przypadku nie zostaną w ogóle zrealizowane.

Zanim zacząłem pracę na projektami na tak niewyobrażalnie dużą skalę, wydawało mi się, że całkiem dobrze znam już język Python, jednak wiele się jeszcze nauczyłem. Te projekty dały mi również możliwość współpracowania z wiodącymi ekspertami w dziedzinie Pythona i uczenia się od nich. Nauczyłem się od nich wszystkiego, od ogólnych zasad dotyczących architektury i projektowania, po różne pomocne wskazówki i sztuczki. W tej książce podzielę się najważniejszymi zdobytymi informacjami, aby pomóc czytelnikom nie tylko w tworzeniu lepszych programów Pythona, lecz także robieniu tego bardziej efektywnie.

Pierwsza wersja tej książki, zatytułowana The Hacker’s Guide to Python, została opublikowana w 2014 roku. Python na poważnie to czwarta edycja, z uaktualnioną oraz całkiem nową zawartością. Życzymy przyjemnej lektury.

Kto i dlaczego powinien przeczytać tę książkę

Ta książka została napisana z myślą o koderach i producentach oprogramowania, którzy chcą jeszcze lepiej poznać język Python.

Przedstawia ona metody i porady, które pomagają w maksymalnym wykorzystaniu możliwości języka Python i projektowaniu przyszłościowych programów. Jeśli pracujesz już nad projektem, to będziesz mieć możliwość od razu zastosować omawiane techniki do udoskonalenia swojego aktualnego kodu. Jeśli rozpoczynasz swój pierwszy projekt, to będziesz mógł utworzyć go zgodnie z najlepszymi praktykami.

Omówimy pewne wewnętrzne elementy języka Python, pomocne w rozwijaniu umiejętności pisania efektywnego kodu. Poznasz wewnętrzne mechanizmy języka, co pomoże Ci w zrozumieniu problemów i nieefektywności.

Książna oferuje również praktyczne, dobrze sprawdzone rozwiązania takich problemów jak testowanie, przenoszenie i skalowanie kodu, aplikacji oraz bibliotek Pythona. Dzięki temu można uczyć się na błędach innych programistów i odkryć strategie, które wspierają długoterminowe utrzymywanie kodu.

O tej książce

Niniejsza książka została zaprojektowana w taki sposób, aby nie trzeba było czytać jej od początku do końca. Możesz przejść od razu do części, które uważasz za najbardziej interesujące lub przydatne w pracy. Książka przedstawia szeroki zakres porad i praktycznych wskazówek. Oto szybki przegląd zawartości poszczególnych rozdziałów.

Rozdział 1 dostarcza wskazówek dotyczących aspektów, które warto wziąć pod uwagę przed rozpoczęciem projektu, takich jak struktura projektu, numerowanie wersji, konfigurowanie automatycznego sprawdzania błędów. Na końcu znajduje się wywiad z Joshuą Harlowem.

Rozdział 2 zawiera wprowadzenie do modułów, bibliotek i platform Pythona oraz zdradza pewne tajniki ich działania. Przedstawia porady dotyczące stosowania modułu sys, czerpania większych korzyści z menedżera pakietów pip, wybierania platformy najlepiej dostosowanej do potrzeb i stosowania bibliotek standardowych oraz zewnętrznych. Rozdział ten zawiera również wywiad z Dougiem Hellmannem.

Rozdział 3 udziela porad dotyczących dokumentowania projektów i zarządzania interfejsami API w miarę jak projekty ewoluują po opublikowaniu. Przedstawione zostaną konkretne zalecenia dotyczące stosowania narzędzia Sphinx do automatyzowania pewnych zadań związanych z dokumentowaniem. W tym rozdziale znaleźć można wywiad z Christophem de Vienne.

Rozdział 4 omawia odwieczny problem stref czasowych i najlepszego obsługiwania ich w programach przy użyciu obiektów datetime oraz tzinfo.

Rozdział 5 pomaga w dostarczaniu oprogramowania użytkownikom, przedstawiając wskazówki dotyczące dystrybucji. Omówione zostaną standardy zarządzania pakietami i ich dystrybucją, biblioteki distutils oraz setuptools, a także łatwe sposoby odkrywania dynamicznych funkcji w pakiecie przy użyciu punktów wejścia. Wywiadu udzielił Nick Coghlan.

Rozdział 6 zawiera porady dotyczące testów jednostkowych (unit tests) z opisem najlepszych praktyk oraz konkretnymi instrukcjami automatyzowania testów jednostkowych przy użyciu pytest. Omówione zostaną również środowiska wirtualne służące do zwiększania izolacji testów. Wywiadu udzielił Robert Collins.

Rozdział 7 zawiera szczegółowe omówienie metod i dekoratorów, ilustrując możliwości zastosowania Pythona do programowania funkcyjnego. W tym rozdziale przedstawimy porady, jak i kiedy używać dekoratorów oraz jak tworzyć dekoratory dla dekoratorów. Przyjrzymy się również metodom statycznym, klasy oraz abstrakcyjnym i pokażemy, jak łączyć wszystkie trzy w celu uzyskania solidniejszego programu.

Rozdział 8 przedstawia pewne techniki programowania funkcyjnego, które można zaimplementować w Pythonie. W tym rozdziale omówione zostaną generatory, wyrażenia listowe, funkcje funkcyjne i typowe narzędzia do ich implementowania, jak również przydatna biblioteka functools.

Rozdział 9 zagląda w głąb języka i omawia drzewo składniowe AST, które stanowi wewnętrzną strukturę Pythona. Zajmuje się również rozszerzaniem flake8 o analizę drzewa AST, co umożliwia sprawdzanie programów w bardziej zaawansowany sposób. Na zakończenie rozdziału przedstawiony zostanie wywiad z Paulem Tagliamonte.

Rozdział 10 oferuje wskazówki dotyczące optymalizowania wydajności przez zastosowanie odpowiednich struktur danych, efektywne definiowanie funkcji oraz przeprowadzanie dynamicznej analizy wydajności w celu zidentyfikowania wąskich gardeł w kodzie. Poruszymy również problem memoizacji oraz redukowania ilości niepotrzebnie kopiowanych danych. Rozdział zawiera wywiad z Victorem Stinnerem.

Rozdział 11 zajmuje się trudnym tematem wielowątkowości, odpowiadając m.in. na pytania: jak i kiedy lepiej jest użyć wielu wątków zamiast wielu procesów oraz czy do tworzenia skalowalnych programów używać architektury zorientowanej na zdarzenia, czy na usługi.

Rozdział 12 zajmuje się relacyjnymi bazami danych. Przyjrzymy się, jak one działają i jak użyć PostgreSQL do efektywnego zarządzania danymi i przesyłania ich strumieniowo. Wywiadu udzielił Dimitri Fontaine.

Na zakończenie, rozdział 13 oferuje solidne porady dotyczące różnych zagadnień, takich jak: zapewnianie zgodności kodu z wersjami Python 2 i 3, tworzenie funkcyjnego kodu przypominającego Lisp, stosowanie menedżerów kontekstu oraz redukowanie liczby powtórzeń za pomocą biblioteki attr.

1

Rozpoczynanie projektu

W pierwszym rozdziale omówimy wybrane  aspekty rozpoczynania projektu i pokażemy,  co warto wcześniej przemyśleć, np. jakiej wersji Pythona użyć, jaką strukturę modeli zastosować, jak efektywnie numerować wersje oprogramowania i jak zastosować najlepsze praktyki programowania w automatycznym wykrywaniu błędów.

Wersje Pythona

Przed rozpoczęciem projektu trzeba zadecydować, która wersja bądź które wersje Pythona będą w nim wspierane. Decyzja ta nie jest wbrew pozorom taka prosta.

Powszechnie wiadomo, że Python obsługuje jednocześnie wiele wersji. Każda podrzędna wersja interpretera otrzymuje 18-miesięczne wsparcie w zakresie naprawiania błędów i 5-letnie wsparcie w zakresie bezpieczeństwa. Na przykład wersja Python 3.7 opublikowana 27 czerwca 2018 roku będzie obsługiwana do czasu opublikowania wersji Python 3.8, co powinno nastąpić koło października 2019 roku. W okolicach grudnia 2019 roku pojawi się ostatnie poprawiające błędy wydanie wersji Python 3.7 i wszyscy powinni dokonać przeniesienia na wersję Python 3.8. Każda nowa wersja Pythona wprowadza nowe funkcje i oznacza pewne starsze funkcje jako przestarzałe (deprecated). Rysunek 1.1 przedstawia linię czasową.

Rysunek 1.1. Linia czasowa publikacji Pythona

Ponadto powinniśmy wziąć pod uwagę problem wersji Python 2 kontra Python 3. Osoby korzystające z (bardzo) starych platform mogą nadal potrzebować obsługi dla wersji Python 2, ponieważ wersja Python 3 nie została udostępniona na tych platformach, ale zasadniczo należy – jeśli to możliwe – zapomnieć o wersji Python 2.

Oto szybki sposób określania, która wersja będzie potrzebna:

• Wersja 2.6 i starsze są oznaczone jako przestarzałe, dlatego nie zalecamy zawracania sobie głowy ich obsługą. Jeśli z jakiegoś powodu zamierzasz obsługiwać w programie te starsze wersje, to miej świadomość, że będziesz mieć trudności z zapewnieniem jednocześnie wsparcia dla wersji Python 3.x. Niestety w starszych systemach możesz nadal napotkać wersję Python 2.6 – wyrazy współczucia, jeśli tak się zdarzy!

• Wersja 2.7 jest i pozostanie ostatnią wersją Python 2.x. Obecnie praktycznie każdy system obsługuje lub może w jakiś sposób obslugiwać wersję Python 3, więc o ile nie zajmujesz się archeologią, nie musisz zaprzątać sobie głowy wersją Python 2.7. Wersja ta przestanie być wspierana po 2020 roku, dlatego zdecydowanie nie warto opierać na niej nowo tworzonym oprogramowania.

• W momencie pisania tej książki wersja 3.7 stanowi najnowszą wersję Python 3 i powinna być preferowaną opcją. Jednak jeśli Twój system operacyjny jest dystrybuowany z wersją 3.6 (większość systemów operacyjnych, z wyjątkiem Windows, zawiera wersję 3.6 lub późniejszą), to upewnij się, że Twoja aplikacja będzie działać również przy użyciu wersji 3.6.

Techniki pisania programów, które obsługują zarówno wersję Python 2.7, jak i 3.x, są omówione w rozdziale 13.

Na zakończenie chcielibyśmy zaznaczyć, że książka ta została napisana z myślą o wersji Python 3.

Układ projektu

Rozpoczynanie nowego projektu zawsze wiąże się z odrobiną niepewności. Nie wiemy jeszcze, jaką strukturę będzie miał nasz projekt, dlatego możemy mieć wątpliwości, jak zorganizować jego pliki. Jednak właściwe zrozumienie najlepszych praktyk pomaga w planowaniu początkowej struktury. Oto kilka wskazówek, jak należy i jak nie należy planować układu projektu.

Co robić

Przede wszystkim przemyśl strukturę projektu, która powinna być dość prosta. Z rozwagą używaj pakietów i hierarchii: nawigowanie po wielopoziomowej hierarchii może być koszmarem, a płaska hierarchia ma tendencje do nadmiernego rozrastania się.

Unikaj często popełnianego błędu polegającego na przechowywaniu testów jednostkowych poza katalogiem pakietu. Te testy zdecydowanie powinny zostać umieszczone w podpakiecie oprogramowania, aby nie zostały przypadkiem automatycznie zainstalowane jako główny moduł tests przez setuptools (lub inną bibliotekę do zarządzania pakietami). Dzięki umieszczeniu w podpakiecie mogą one być instalowane i w przyszłości wykorzystywane przez inne pakiety, umożliwiając użytkownikom projektowanie własnych testów jednostkowych.

Rysunek 1.2 ilustruje, jak powinna wyglądać standardowa hierarchia plików.

Standardowa nazwa skryptu instalacyjnego Pythona to setup.py. Towarzyszy mu plik setup.cfg, który powinien zawierać szczegóły konfiguracji skryptu instalacyjnego. Po uruchomieniu plik setup.py zainstaluje nasz pakiet przy użyciu narzędzi do dystrybucji Pythona.

Możemy również przekazać użytkownikom najważniejsze informacje, umieszczając je w pliku o nazwie README.rst (lub README.txt albo dowolnej innej nazwie). Ponadto katalog docs powinien zawierać dokumentację pakietu w formacie reStructuredText, który zostanie wykorzystany przez narzędzie Sphinx (patrz rozdział 3).

Pakiety będą często musiały zawierać dodatkowe dane do wykorzystania w oprogramowaniu, takie jak obrazy, skrypty powłoki itp. Niestety nie ma powszechnie uznanego standardu, który określałby miejsce przechowywania tych plików, dlatego umieszczamy je w tym miejscu, które ma największy sens z perspektywy ich funkcji w projekcie. Na przykład szablony aplikacji WWW można umieszczać w katalogu templates, w katalogu głównym pakietu.

Rysunek 1.2. Standardowy katalog pakietu

Oto inne często stosowane katalogi najwyższego poziomu:

• etc z przykładowymi plikami konfiguracji,

• tools ze skryptami powłoki lub powiązanymi narzędziami,

• bin z napisanymi przez nas skryptami binarnymi, które zostaną zainstalowane przez setup.py.

Czego nie robić

Istnieje pewien błąd projektowy, jaki często spotykam w strukturach projektów, które nie zostały do końca przemyślane: niektórzy programiści tworzą pliki i moduły, opierając się na typie kodu, jaki będzie się w nich znajdował. Na przykład tworzą pliki funkcje.py lub wyjątki.py. Jest to fatalne podejście i nie ułatwia innym programistom nawigowania po kodzie. Czytając kod, programista oczekuje, że obszar funkcjonalny programu będzie zawarty w jednym pliku. Podejście, które bez powodu zmusza czytających do przeskakiwania między plikami, nie sprzyja organizacji kodu.

Kod należy organizować na podstawie funkcji, a nie typów.

Kolejnym złym pomysłem jest tworzenie katalogu modułu, który zawiera jedynie plik __init__.py, ponieważ reprezentuje on niepotrzebne zagnieżdżanie. Na przykład nie należy tworzyć katalogu o nazwie hooks z jednym plikiem o nazwie hooks/__init__.py, gdy wystarczy plik hooks.py. Jeśli tworzymy katalog, to powinien on zawierać kilka innych plików Pythona, które należą do kategorii reprezentowanej przez katalog. Niepotrzebne budowanie głębokiej hierarchii jest mylące.

Ponadto należy zachować szczególną ostrożność, umieszczając kod w pliku __init__.py. Ten plik zostanie wywołany i wykonany przy pierwszym załadowaniu danego modułu. Umieszczenie niewłaściwego kodu w pliku __init__.py może pociągać z sobą niepożądane skutki uboczne. W rzeczywistości pliki __init__.py lepiej z reguły pozostawić puste, chyba że wiemy, co robimy. Jednak nie należy całkowicie usuwać plików __init__.py, gdyż w przeciwnym wypadku nie będzie można w ogólne zaimportować modułu Pythona: w języku Python katalog musi zawierać plik __init__.py, aby mógł być traktowany jak podmoduł.

Numerowanie wersji

Wersje oprogramowania muszą zostać oznaczone tak, aby użytkownicy wiedzieli, która wersja jest najnowsza. W każdym projekcie użytkownicy muszą mieć możliwość opracowania linii czasowej dla ewoluującego kodu.

Istnieje nieskończona liczba sposobów organizowania numerów wersji. Jednak specyfikacja PEP 440 wprowadza format wersji, który należy stosować w każdym pakiecie Pythona, a najlepiej w każdej aplikacji, aby inne programy i pakiety mogły łatwo i trafnie określać, która wersja pakietu jest potrzebna.

PEP 440 definiuje następującą postać wyrażenia regularnego do numerowania wersji:

N[.N]+[{a|b|c|rc}N][.postN][.devN]

To wyrażenie obsługuje standardowe numerowanie, takie jak 1.2 czy 1.2.3. Warto zwrócić uwagę na kilka szczegółów:

• Wersja 1.2 to odpowiednik 1.2.0, a 1.3.4 to odpowiednik 1.3.4.0, itd.

• Wersje pasujące do wyrażenia N[.N]+ stanowią wydania ostateczne.

• Wersje oparte na dacie, takie jak 2013.06.22, są uznawane za nieprawidłowe. Automatyczne narzędzia, zaprojektowane z myślą o wykrywaniu numerów wersji w formacie PEP 440, zgłoszą (bądź powinny zgłosić) błąd, jeśli wykryją numer wersji większy lub równy 1980.

• Ostatni składnik może również mieć następujący format:

– N[.N]+aN (np. 1.2a1) oznacza wydanie alfa, czyli wersję, która może nie być stabilna lub może nie zawierać pewnych funkcji.

– N[.N]+bN (np. 2.3.1b2) oznacza wydanie beta, czyli wersję, która może oferować wszystkie funkcje, ale również zawierać pewne błędy.

– N[.N]+cN lub N[.N]+rcN (np. 0.4rc1) oznacza kandydata do wydania, czyli wersję, która może zostać wydana jako produkt ostateczny, o ile nie ujawnią się jakieś istotne problemy. Przedrostki rc oraz c mają to samo znaczenie, ale jeśli występują obok siebie, wydania rc uznawane są za nowsze niż wydania c.

• Można użyć również następujących przyrostków:

– Przyrostek .postN (np. 1.4.post2) jest używany w wersjach następujących po wydaniu oprogramowania, które służą zazwyczaj do poprawiania drugorzędnych błędów powstałych w procesie publikacji, takich jak pomyłki w dokumentacji wydania. Wydając wersję z poprawkami programu, zamiast stosować przyrostek .postN, należy zwiększyć pomocniczy składnik numeru wersji.

– Przyrostek .devN (np. 2.3.4.dev3) oznacza wydanie deweloperskie. Identyfikuje wydanie poprzedzające określoną wersję, np. 2.3.4.dev3, i oznacza trzecią deweloperską wersję wydania 2.3.4, przed jakimkolwiek wydaniem alfa, beta, kandydującym lub ostatecznym. Odradza się stosowanie tego przyrostka, ponieważ jest on mniej zrozumiały
dla ludzi.

Ten schemat powinien okazać się wystarczający w większości przypadków.

Być może słyszałeś o specyfikacji Semantic Versioning, która przedstawia osobne wytyczne dotyczące numerowania wersji. Ta specyfikacja częściowo pokrywa się ze specyfikacją PEP 440, ale niestety nie jest z nią w pełni zgodna. Na przykład specyfikacja Semantic Versioning zaleca numerowanie wydań poprzedzających ostateczne przy użyciu schematu 1.0.0-alpha+001, który nie jest zgodny ze specyfikacją PEP 440.

Wiele rozproszonych systemów kontroli wersji (Distributed Version Control System – DVCS), takich jak Git czy Mercurial, umożliwia generowanie numerów wersji przy użyciu identyfikującego skrótu (patrz git describe dla programu Git). Niestety ten system nie jest zgodny ze schematem zdefiniowanym w specyfikacji PEP 440, między innymi dlatego, że identyfikujących skrótów nie można sortować.

Style programowania i automatyczne sprawdzanie

Style programowania stanowią delikatny temat, jednak warto go poruszyć przed zagłębieniem się w szczegóły języka Python. W odróżnieniu od wielu innych języków programowania, do definiowania bloków w Pythonie używa się wcięć. Choć takie rozwiązanie dostarcza prostą odpowiedź na odwieczne pytanie „Gdzie umieścić nawiasy klamrowe?”, pociąga za sobą nowe pytanie: „W jaki sposób stosować wcięcia?”

Było to jedno z pierwszych pytań zadanych przez społeczność programistów, dlatego twórcy Pythona, bardzo roztropnie, opublikowali specyfikację PEP 8: Style Guide for Python Code (https://www.python.org/dev/peps/pep-0008/).

Ten dokument definiuje standardowy styl pisania kodu Pythona. Lista zaleceń sprowadza się do następujących:

• Należy używać czterech spacji dla każdego poziomu wcięcia.

• Wiersze powinny zawierać maksymalnie 79 znaków.

• Definicje funkcji najwyższego poziomu i definicje klas należy rozdzielać dwoma pustymi wierszami.

• W plikach powinno się stosować kodowanie ASCII lub UTF-8.

• Każdy moduł należy importować w osobnej instrukcji import i osobnym wierszu. Instrukcje importowania należy umieszczać na początku pliku, po komentarzach i ciągach dokumentacyjnych, grupując je na standardowe, następnie zewnętrzne, a na zakończenie lokalne biblioteki.

• Nie należy umieszczać niepotrzebnych spacji w nawiasach okrągłych, kwadratowych bądź klamrowych ani przed przecinkami.

• Wyrazy w nazwach klas powinny rozpoczynać się wielką literą (np. CamelCase), wyjątki powinny zawierać przyrostek Error (jeśli są błędami), a nazwy funkcji powinny składać się z wyrazów rozpoczynających się małą literą oraz podkreśleń (np. wyrazy_oddzielone_podkresleniami). Natomiast _prywatne atrybuty oraz metody powinny rozpoczynać się od podkreślenia.

Te zalecenia nie są trudne w implementacji i mają sens. Większość programistów Pythona nie ma problemów z ich przestrzeganiem podczas pisania kodu.

Jednak błądzić jest rzeczą ludzką, a analizowanie kodu pod kątem przestrzegania zaleceń specyfikacji PEP 8 nie należy do przyjemności. Na szczęście istnieje narzędzie pep8 (dostępne na stronie: https://pypi.org/project/pep8/), które może automatycznie sprawdzić dowolny plik Pythona, jaki do niego prześlemy. Wystarczy zainstalować narzędzie pep8 przy użyciu pip, aby móc stosować je na dowolnym pliku w następujący sposób:

$ pep8 hello.py
hello.py:4:1: E302 expected 2 blank lines, found 1
$ echo $?
1

W tym przykładzie użyliśmy pep8 na przykładowym pliku hello.py. Wynik wskazuje, które wiersze i kolumny nie są zgodne ze specyfikacją PEP 8 i zgłasza każdy problem z kodem – w tym wypadku w wierszu 4 i kolumnie 1. Naruszenia reguł MUST w specyfikacji są zgłaszane jako błędy, których kod błędu rozpoczyna się od E. Natomiast drugorzędne problemy są zgłaszane jako ostrzeżenia, których kod błędu rozpoczyna się od W. Trzycyfrowy kod, następujący po pierwszej literze, precyzuje typ błędu lub ostrzeżenia.

Cyfra setek wskazuje ogólną kategorię kodu błędu np. błędy rozpoczynające się od E2 sygnalizują problemy ze spacjami, błędy rozpoczynające się od E3 sygnalizują problemy z pustymi wierszami, a ostrzeżenia rozpoczynające się od W6 sygnalizują użycie funkcji oznaczonej jako przestarzała. Listę wszystkich kodów znaleźć można w dokumentacji readthedocs narzędzia pep8 (https://pep8.readthedocs.io/).