Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00408 005361 18980545 na godz. na dobę w sumie
Czysty kod w Pythonie - książka
Czysty kod w Pythonie - książka
Autor: Liczba stron: 176
Wydawca: Helion Język publikacji: polski
ISBN: 978-83-283-6462-2 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> python - programowanie
Porównaj ceny (książka, ebook (-30%), audiobook).

W porównaniu z innymi językami programowania Python wyróżnia się prostotą i zaskakującymi możliwościami. Używa się go do analizy danych, budowania sztucznej inteligencji, tworzenia stron WWW, jak również w badaniach naukowych. Właściwości tego języka sprawiają, że kod trzeba pisać uważnie, szczególnie w dużych projektach. Tymczasem trudno jest znaleźć odpowiednio dobre źródło informacji o sposobach tworzenia kodu wysokiej jakości. Publikacje dotyczące dobrych praktyk kodowania w Pythonie są nieliczne, a ich jakość nieraz pozostawia wiele do życzenia. Dodatkowy problem wynika z wszechstronności Pythona: jest wykorzystywany w wielu dziedzinach i trudno wskazać wspólne dla nich wzorce programistyczne.

Ta książka jest znakomitym przewodnikiem, dzięki któremu zaczniesz tworzyć wydajne i bezbłędne aplikacje w Pythonie. Zaczniesz od formatowania i dokumentowania kodu, umiejętnego stosowania wbudowanych struktur, stosowania modułów i metaklas. W ten sposób nauczysz się pisać uporządkowany kod. Potem poznasz nowe funkcjonalności języka Python i dowiesz się, jak efektywnie z nich korzystać. Następnie dowiesz się, jak wykorzystywać zaawansowane cechy języka, takie jak programowanie asynchroniczne, określanie typów danych i obsługa ścieżek, a także jak diagnozować kod, wykonywać testy jednostkowe i integracyjne oraz przygotowywać kod do uruchomienia w środowisku produkcyjnym. Na końcu poznasz najważniejsze narzędzia przeznaczone do szybkiego tworzenia kodu, zarządzania jego wersjami i weryfikowania poprawności.

W tej książce między innymi:

Python: kodowanie jest sztuką!

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

Tytuł oryginału: Clean Python: Elegant Coding in Python Tłumaczenie: Andrzej Watrak ISBN: 978-83-283-6462-2 First published in English under the title Clean Python; Elegant Coding in Python by Sunil Kapil, edition: 1 Copyright © Sunil Kapil, 2019 This edition has been translated and published under licence from APress Media, LLC, part of Springer Nature. APress Media, LLC, part of Springer Nature takes no responsibility and shall not be made liable for the accuracy of the translation. Polish edition copyright © 2020 by Helion SA. All rights reserved. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Helion SA dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Helion SA nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Helion SA ul. Kościuszki 1c, 44-100 Gliwice tel. 32 231 22 19, 32 230 98 63 e-mail: helion@helion.pl WWW: http://helion.pl (księgarnia internetowa, katalog książek) Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie/czykop Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję. Pliki z przykładami omawianymi w książce można znaleźć pod adresem: ftp://ftp.helion.pl/przyklady/czykop.zip Printed in Poland. • Kup książkę • Poleć książkę • Oceń książkę • Księgarnia internetowa • Lubię to! » Nasza społeczność Spis treści Rozdział 1 O autorze ...............................................................................................7 O korektorze merytorycznym .................................................................8 Podziękowania ......................................................................................9 Wprowadzenie ....................................................................................11 Kodowanie pythoniczne ......................................................................13 Tworzenie pythonicznego kodu ..................................................................................13 Nazewnictwo ...........................................................................................................14 Wyrażenia i instrukcje ...........................................................................................16 Pythoniczny styl kodowania .................................................................................19 Komentarze dokumentacyjne ......................................................................................24 Komentarze dokumentacyjne do modułów .......................................................26 Komentarze dokumentacyjne do klas .................................................................26 Komentarze dokumentacyjne do funkcji ............................................................27 Przydatne narzędzia dokumentacyjne .................................................................28 Pythoniczne struktury sterujące ..................................................................................28 Wyrażenia listowe ...................................................................................................28 Nie twórz skomplikowanych wyrażeń listowych ...............................................29 Kiedy stosować wyrażenia lambda? .....................................................................31 Kiedy stosować generatory, a kiedy wyrażenia listowe? ...................................31 Dlaczego nie należy stosować instrukcji else w pętlach? ..................................32 Dlaczego warto stosować funkcję range() w języku Python 3? ........................34 Zgłaszanie wyjątków .....................................................................................................35 Często zgłaszane wyjątki ........................................................................................36 Obsługuj wyjątki za pomocą instrukcji finally ...................................................37 Twórz własne klasy wyjątków ...............................................................................37 Obsługuj konkretne wyjątki ..................................................................................39 Poleć książkęKup książkę SPIS TREŚCI Rozdział 2 Rozdział 3 Zwracaj uwagę na zewnętrzne wyjątki ................................................................40 Twórz jak najmniejsze bloki try ...........................................................................41 Podsumowanie ...............................................................................................................42 Struktury danych .................................................................................43 Popularne struktury danych ........................................................................................43 Zbiory i wydajny kod .............................................................................................43 Przetwarzanie danych za pomocą struktury namedtuple ................................45 Typ str i znaki diakrytyczne ..................................................................................47 Zamiast list stosuj iteratory ...................................................................................48 Przetwarzaj listy za pomocą funkcji zip() ...........................................................50 Wykorzystuj zalety wbudowanych funkcji .........................................................51 Zalety słownika ..............................................................................................................53 Kiedy używać słownika zamiast innych struktur? .............................................53 Kolekcje ....................................................................................................................53 Słowniki uporządkowany, domyślny i zwykły ...................................................56 Słownik jako odpowiednik instrukcji switch ......................................................57 Scalanie słowników .................................................................................................58 Czytelne wyświetlanie zawartości słownika ........................................................59 Podsumowanie ...............................................................................................................60 Jak pisać lepsze funkcje i klasy? ..........................................................61 Funkcje ............................................................................................................................61 Twórz małe funkcje ................................................................................................61 Twórz generatory ....................................................................................................63 Używaj wyjątku zamiast wyniku None ...............................................................64 Stosuj w argumentach klucze i wartości domyślne ...........................................65 Nie twórz funkcji jawnie zwracających wynik None .........................................66 Krytycznie podchodź do tworzonych funkcji ....................................................68 Stosuj w wyrażeniach funkcje lambda .................................................................70 Klasy ................................................................................................................................71 Jak duża powinna być klasa? .................................................................................71 Struktura klasy ........................................................................................................72 Właściwe użycie dekoratora @property ..............................................................74 Kiedy należy stosować metody statyczne? ..........................................................75 Dziedziczenie klas abstrakcyjnych .......................................................................76 Odwołania do stanu klasy przy użyciu dekoratora @classmethod .................77 Atrybuty publiczne zamiast prywatnych ............................................................78 Podsumowanie ...............................................................................................................79 Rozdział 4 Praca z modułami i metaklasami .........................................................81 Moduły i metaklasy .......................................................................................................81 Porządkowanie kodu za pomocą modułów ...............................................................82 Zalety pliku __init__.py ................................................................................................84 4 Poleć książkęKup książkę Rozdział 5 Rozdział 6 SPIS TREŚCI Importowanie funkcji i klas z modułów ....................................................................86 Blokowanie importu całego modułu za pomocą metaklasy __all__ ...............87 Kiedy stosować metaklasy? ..........................................................................................88 Weryfikowanie podklas za pomocą metody __new__() ..........................................89 Dlaczego atrybut __slots__ jest tak przydatny? ........................................................91 Modyfikowanie funkcjonowania klasy za pomocą metaklasy ................................93 Deskryptory w języku Python ......................................................................................94 Podsumowanie ...............................................................................................................96 Dekoratory i menedżery kontekstu ......................................................97 Dekoratory ......................................................................................................................97 Czym są dekoratory i dlaczego są tak przydatne? ..............................................98 Korzystanie z dekoratorów ...................................................................................99 Modyfikowanie działania funkcji za pomocą dekoratorów .......................... 101 Stosowanie kilku dekoratorów jednocześnie ................................................... 102 Dekorowanie funkcji z argumentami ............................................................... 103 Używaj dekoratorów z biblioteki ....................................................................... 104 Dekoratory obsługujące stan klasy i weryfikujące poprawność danych ...... 106 Menedżery kontekstu ................................................................................................. 108 Zalety menedżerów kontekstu ........................................................................... 108 Tworzenie menedżera kontekstu od podstaw ................................................. 109 Tworzenie menedżera kontekstu za pomocą biblioteki contextlib .............. 111 Praktyczne przykłady użycia menedżera kontekstu ....................................... 111 Podsumowanie ............................................................................................................ 114 Generatory i iteratory ........................................................................115 Zalety generatorów i iteratorów ............................................................................... 115 Iteratory ................................................................................................................. 115 Generatory ............................................................................................................ 117 Kiedy stosować iteratory? ................................................................................... 118 Moduł itertools .................................................................................................... 119 Dlaczego generatory są tak przydatne? ............................................................ 121 Wyrażenia listowe i iteratory ............................................................................. 122 Zalety instrukcji yield ................................................................................................. 122 Instrukcja yield from ........................................................................................... 123 Instrukcja yield jest szybka ................................................................................. 123 Podsumowanie ............................................................................................................ 124 Rozdział 7 Nowe funkcjonalności języka Python ................................................125 Programowanie asynchroniczne .............................................................................. 125 Wprowadzenie do programowania asynchronicznego ................................. 126 Jak to działa? ......................................................................................................... 128 Obiekty oczekiwalne ........................................................................................... 133 Biblioteki do tworzenia kodu asynchronicznego ............................................ 139 5 Poleć książkęKup książkę Python i typy danych ................................................................................................. 143 Typy danych w Pythonie .................................................................................... 143 Moduł typing ........................................................................................................ 144 Czy typy danych spowalniają kod? ................................................................... 145 Jak dzięki modułowi typing można pisać lepszy kod? ................................... 146 Metoda super() ............................................................................................................ 147 Lepsza obsługa ścieżek dzięki bibliotece pathlib .................................................... 147 print() jest teraz funkcją ............................................................................................ 147 f-ciągi ............................................................................................................................ 147 Obowiązkowe argumenty pozycyjne ....................................................................... 148 Kontrolowana kolejność elementów w słownikach .............................................. 148 Iteracyjne rozpakowywanie struktur ....................................................................... 149 Podsumowanie ............................................................................................................ 149 Diagnostyka i testy kodu ...................................................................151 Diagnostyka ................................................................................................................. 151 Narzędzia diagnostyczne .................................................................................... 152 Funkcja breakpoint() ........................................................................................... 155 Moduł logging zamiast funkcji print() ............................................................. 155 Identyfikowanie słabych punktów kodu za pomocą metryk ........................ 159 Do czego przydaje się środowisko IPython? .................................................... 159 Testy ............................................................................................................................. 161 Dlaczego testowanie kodu jest ważne? ............................................................. 161 Biblioteki pytest i unittest ................................................................................... 161 Testowanie oparte na właściwościach .............................................................. 164 Tworzenie raportów z testów ............................................................................. 165 Automatyzacja testów jednostkowych ............................................................. 166 Przygotowanie kodu do uruchomienia w środowisku produkcyjnym ........ 166 Sprawdzanie pokrycia kodu testami ................................................................. 167 Program virtualenv .............................................................................................. 168 Podsumowanie ............................................................................................................ 169 Niezwykłe narzędzia dla języka Python .............................................171 Sphinx ........................................................................................................................... 171 Coverage.py ................................................................................................................. 172 pre-commit .................................................................................................................. 173 Pyenv ............................................................................................................................ 173 Jupyter Lab ................................................................................................................... 174 Pycharm/VSCode/Sublime ....................................................................................... 174 Flake8 i Pylint .............................................................................................................. 175 SPIS TREŚCI Rozdział 8 Dodatek 6 Poleć książkęKup książkę R O Z D Z I A Ł 1    Kodowanie pythoniczne Tym, co odróżnia Pythona od innych języków programowania, jest prostota, ale o dużej głębi. Z racji prostoty tego języka bardzo ważne jest, aby kod pisać uważnie, szczególnie w dużych projektach, ponieważ można go łatwo skomplikować i nadmiernie rozbudować. Python ma swoją filozofię zen, która przedkłada prostotę nad złożoność1. W tym rozdziale poznasz kilka popularnych praktyk, dzięki którym Twój kod będzie prostszy i bardziej czytelny. Opisanych jest tutaj kilka znanych i mniej znanych technik. Pamiętaj o nich podczas pracy nad obecnymi i przyszłymi projektami, a Twój kod będzie lepszy.  Uwaga Jeżeli będziesz postępował zgodnie z filozofią zen Pythona, Twój kod będzie „pythoniczny”. W oficjalnej dokumentacji tego języka opisanych jest mnóstwo dobrych praktyk, dzięki którym kod jest bardziej przejrzysty i czytelny. Lektura dokumentacji PEP8 z pewnością pomoże Ci zrozumieć, dlaczego stosowanie niektórych praktyk jest szczególnie zalecane. Tworzenie pythonicznego kodu Oficjalna dokumentacja PEP8, dostępna na stronie https://www.python.org/dev/peps/pep-0008, opisuje wiele dobrych praktyk pisania kodu w języku Python, które od czasu ich powstania uległy dużym zmianom. W tym rozdziale skupimy się na kilku z nich oraz na korzyściach, jakie ma z nich programista. 1 https://www.python.org/dev/peps/pep-0020 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Nazewnictwo Jestem programistą kodującym w różnych językach, m.in. Java, NodeJS, Perl i Golang. W każdym z nich obowiązują konwencje nazewnictwa zmiennych, funkcji, klas itp. W Pythonie również jest zalecane stosowanie pewnych konwencji. W tej części rozdziału omówionych jest kilka z nich, których powinieneś przestrzegać, pisząc swój kod. Zmienne i funkcje W nazwach zmiennych i funkcji stosuj małe litery, a słowa oddzielaj znakiem podkreślenia. Dzięki temu będą bardziej czytelne, jak w listingu 1.1. Listing 1.1. Nazwy zmiennych names = Python # Nazwa zmiennej job_title = Programista # Nazwa zmiennej ze znakiem podkreślenia populated_countries_list = [] # Nazwa zmiennej # ze znakiem podkreślenia Stosuj również niekonfliktowe nazwy, umieszczając przed nimi jeden lub dwa znaki podkreślenia, jak w listingu 1.2. Listing 1.2. Niekonfliktowe nazwy _books = {} # Definicja prywatnej zmiennej __dict = [] # Nazwa niekolidująca z innymi nazwami we wbudowanych bibliotekach Pojedynczy znak podkreślenia stosuj jako prefiks w nazwach wewnętrznych zmiennych, do których nie mogą mieć dostępu inne klasy. To jest tylko konwencja, ponieważ umieszczenie pojedynczego znaku podkreślenia nie definiuje zmiennej jako prywatnej. W nazwach funkcji też obowiązują konwencje pokazane w listingu 1.3. Listing 1.3. Nazwy zwykłych funkcji def get_data(): ... def calculate_tax_data(): ... W przypadku metod obowiązują takie same zasady pozwalające uniknąć konfliktu nazw z wbudowanymi funkcjami (patrz listing 1.4). 14 Poleć książkęKup książkę TWORZENIE PYTHONICZNEGO KODU Listing 1.4. Niekonfliktowe nazwy prywatnych metod # Prywatna metoda z pojedynczym znakiem podkreślenia def _get_data(): ... # Podwójny znak podkreślenia zapobiegający konfliktowi z nazwami wbudowanych funkcji def __path(): ... Oprócz przestrzegania powyższych zasad ważne jest nadawanie funkcjom i zmiennym zrozumiałych nazw. Przeanalizujmy przykład funkcji zwracającej obiekt użytkownika o danym identyfikatorze (patrz listing 1.5). Listing 1.5. Nazwy funkcji # Zła nazwa def get_user_info(id): db = get_db_connection() user = execute_query_for_user(id) return user # Dobra nazwa def get_user_by(user_id): db = get_db_connection() user = execute_user_query(user_id) return user Przeznaczenie drugiej funkcji, get_user_by(), jest oczywiste, ponieważ jej nazwa odnosi się do argumentu. Natomiast nazwa pierwszej funkcji, get_user_info(), jest niezrozumiała, ponieważ argument może oznaczać cokolwiek, na przykład indeks użytkownika, identyfikator płatności lub coś innego. Tego rodzaju kod będzie niezrozumiały dla innych programistów. Aby go poprawić, zmieniłem nazwę funkcji i jej argumentu, dzięki czemu kod stał się bardziej czytelny. Teraz od razu wiadomo, jakie jest przeznaczenie drugiej funkcji i jaki zwraca ona wynik Twoim obowiązkiem jest staranne dobieranie nazw zmiennych i funkcji, aby kod był czytelny dla innych programistów. Klasy Tak jak w większości języków, nazwy klas powinny składać się z wielkich i małych liter. W listingu 1.6 przedstawiono prosty przykład. 15 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Listing 1.6. Nazwy klas class UserInformation: def get_user(id): db = get_db_connection() user = execute_query_for_user(id) return user Stałe Nazwy stałych powinny składać się wyłącznie z wielkich liter, jak w przykładowym listingu 1.7. Listing 1.7. Nazwy stałych TOTAL = 56 TIMOUT = 6 MAX_OVERFLOW = 7 Nazwy argumentów funkcji i metod Zasady nazewnictwa argumentów funkcji i metod są takie same jak w przypadku zmiennych i metod. Metoda różni się od funkcji tym, że jej pierwszym argumentem jest self (patrz listing 1.8). Listing 1.8. Nazwy argumentów funkcji i metod def calculate_tax(amount, yearly_tax): ... class Player: def get_total_score(self, player_name): ... Wyrażenia i instrukcje Zapewne w celu zmniejszenia liczby wierszy i zaimponowania kolegom próbowałeś pisać kod na różne zmyślne sposoby. Jednak na takim podejściu traci czytelność i prostota kodu. Przeanalizujmy kod z listingu 1.9 sortującego zagnieżdżony słownik. Listing 1.9. Sortowanie zagnieżdżonego słownika users = [{ first_name : Helena , age :39}, { first_name : Bartek , age :10}, { first_name : Ania , age :9} ] users = sorted(users, key=lambda user: user[ first_name ].lower()) 16 Poleć książkęKup książkę TWORZENIE PYTHONICZNEGO KODU Co jest złego w tym kodzie? Za pomocą jednowierszowej funkcji lambda słownik jest sortowany według klucza first_name. Jest to bardziej pomysłowy sposób niż stosowanie pętli. Jednak trudno jest, szczególnie innemu programiście, ocenić na pierwszy rzut oka, co się w kodzie dzieje. Powodem jest funkcja lambda, która nie jest prostym pojęciem, a do tego ma skomplikowaną składnię. Oczywiście dzięki niej można pomysłowo posortować słownik, a kod składa się z mniejszej liczby wierszy, ale to nie oznacza, że jest poprawny i czytelny. Nie będzie działał poprawnie, jeżeli słownik będzie zawierał błędne wartości lub zabraknie w nim kluczy. Aby kod działał poprawnie i był bardziej czytelny, zastosujmy w nim funkcję, która będzie sprawdzała, czy wartości są poprawne. Funkcja ta jest bardzo prosta (patrz listing 1.10). Listing 1.10. Sortowanie słownika za pomocą funkcji users = [{ first_name : Helena , age :39}, { first_name : Bartek , age :10}, { first_name : Ania , age :9} ] def get_user_name(users): Odczytanie imienia i zamiana liter na małe. return users[ first_name ].lower() def get_sorted_dictionary(users): Sortowanie zagnieżdżonego słownika. if not isinstance(users, dict): raise ValueError( Błędna wartość w słowniku ) if not len(users): raise ValueError( Słownik jest pusty ) users_by_name = sorted(users, key=get_user_name) return users_by_name Powyższy kod sprawdza poprawność wartości i jest o wiele bardziej czytelny niż w poprzedniej wersji z jednowierszowym wyrażeniem, które wprawdzie zmniejszało liczbę wierszy, ale znacznie komplikowało kod. Nie oznacza to oczywiście, że wyrażenia jednowierszowe są złe. Uważam jednak, że należy unikać ich stosowania, jeżeli pogarszają czytelność kodu. Podczas pisania kodu decyzje takie jak powyższa będziesz podejmował nieustannie. Czasami dzięki jednowierszowym wyrażeniom kod jest bardziej czytelny, czasami mniej. Rozważmy przykład kodu odczytującego zawartość pliku CSV i zliczającego jego wiersze. Listing 1.11 pokazuje, dlaczego ważna jest czytelność kodu i stosowanie w nim zrozumiałych nazw. Dzięki wyodrębnieniu funkcji pomocniczej kod jest bardziej czytelny, a w środowisku produkcyjnym łatwiejszy w diagnozowaniu problemów. 17 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Listing 1.11. Odczytywanie pliku CSV import csv with open( pracownicy.csv , mode= r ) as csv_file: csv_reader = csv.DictReader(csv_file) line_count = 0 for row in csv_reader: if line_count == 0: print(f Nazwy kolumn: { , .join(row)} ) line_count += 1 print(f \t{row[ name ]} wynagrodzenie: {row[ salary ]} f data urodzenia {row[ birthday ]}. ) line_count += 1 print(f Przetworzono {line_count} wierszy. ) Wewnątrz instrukcji with wykonywanych jest kilka operacji. Aby kod był bardziej czytelny i mniej podatny na błędy, można fragment przetwarzający zawartość pliku wyodrębnić do postaci osobnej funkcji. Trudno diagnozować kod, w którym w kilku wierszach jest wykonywanych wiele operacji, dlatego warto tworzyć funkcje o jasno określonym przeznaczeniu. Listing 1.12 przedstawia kod podzielony na części. Listing 1.12. Bardziej czytelny kod przetwarzający plik CSV import csv def process_salary(csv_reader): Przetworzenie pliku CSV. global line_count for row in csv_reader: if line_count == 0: print(f Nazwy kolumn: { , .join(row)} f data urodzenia {row[ birthday ]}. ) line_count += 1 print(f \t{row[ name ]} wynagrodzenie: {row[ salary ]} ) line_count += 1 print(f Przetworzono {line_count} wierszy. ) with open( pracownicy.csv , mode= r ) as csv_file: csv_reader = csv.DictReader(csv_file) line_count = 0 process_salary(csv_reader) Powyższy kod zamiast wykonywać wszystkie operacje wewnątrz instrukcji with wykorzystuje funkcję pomocniczą. Teraz widać wyraźnie, co robi funkcja process_salary(). Gdyby trzeba było obsłużyć określony wyjątek lub przetworzyć inne dane zawarte w pliku CSV, można powyższą funkcję podzielić na kolejne części zgodnie z zasadą pojedynczej odpowiedzialności. 18 Poleć książkęKup książkę TWORZENIE PYTHONICZNEGO KODU Pythoniczny styl kodowania Dokumentacja PEP8 zawiera zalecenia, dzięki którym kod jest bardziej przejrzysty i czytelny. Przeanalizujmy kilka z nich. Stosuj metodę join() zamiast operatora łączenia ciągów znaków Jeżeli ważna jest wydajność kodu, stosuj metodę join() zamiast operatora łączenia ciągów, np. a += b lub a = a + b (patrz listing 1.13). Metoda join()gwarantuje szybsze łączenia ciągów w różnych implementacjach Pythona, ponieważ przydziela pamięć na połączone ciągi tylko raz. Natomiast w przypadku operatora pamięć jest przydzielana dla każdego ciągu osobno, ponieważ w Pythonie ciągi są niemutowalne. Listing 1.13. Użycie metody join() first_name = Jan last_name = Nowak # Niezalecany sposób łączenia ciągów. full_name = first_name + + last_name # Bardziej wydajny i czytelny sposób. .join([first_name, last_name]) Do sprawdzania wartości None stosuj instrukcje is oraz is not Do sprawdzania wartości None stosuj instrukcje is oraz is not. Pamiętaj o tym, pisząc na przykład następujący kod: if val: # Instrukcja nie będzie działać poprawnie, jeżeli val będzie miała wartość None Zmienna val może być typu kontenerowego, na przykład dict lub set. Przeanalizujmy dokładniej przypadki, które mogą Cię zaskoczyć. Jeżeli w powyższym przykładzie zmienna val będzie zawierała pusty słownik, zostanie potraktowana tak, jakby zawierała wartość false, co może nie być pożądanym efektem. Dlatego zachowaj ostrożność, pisząc kod w ten sposób. Nie rób tak: val = {} if val: # Wyrażenie przyjmie wartość false Zawsze warunki definiuj jak najściślej, dzięki czemu kod będzie mniej podatny na błędy. Rób tak: if val is not None: # Wyrażenie przyjmie wartość false tylko wtedy, gdy zmienna będzie miała # wartość None 19 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Stosuj warunek is not zamiast not … is Nie ma różnicy pomiędzy warunkami is not a not ... is. Jednak pierwszy zapis jest bardziej czytelny. Nie rób tak: if not val is None: Tylko tak: if val is not None: W instrukcjach przypisania stosuj zwykłą funkcję zamiast funkcji lambda W instrukcji przypisania stosuj zwykłą funkcję zamiast funkcji lambda. Za pomocą słowa kluczowego lambda można kodować operacje w jednym wierszu. Czasami jednak lepszym rozwiązaniem jest zdefiniowanie funkcji za pomocą instrukcji def. Nie rób tak: square = lambda x: x * x Tylko tak: def square(val): return val * val Wynik funkcji square() łatwiej jest przekształcić w ciąg znaków, jak również łatwiej jest diagnozować tę funkcję niż lambda. Z tego powodu wyrażenia tego rodzaju przestają być przydatne. Funkcję lambda stosuj w większych wyrażeniach, o ile nie wpłynie to na czytelność kodu. Konsekwentnie stosuj instrukcję return Jeżeli funkcja zwraca wynik, sprawdzaj, czy robi to we wszystkich swoich wątkach. Dobrą praktyką jest umieszczanie instrukcji return w każdym miejscu, w którym funkcja kończy działanie. Funkcje, które jedynie wykonują określone operacje, domyślnie zwracają wynik None. Nie rób tak: def calculate_interest(principle, time, rate): if principle 0: return (principle * time * rate) / 100 def calculate_interest(principle, time, rate): if principle 0: return return (principle * time * rate) / 100 20 Poleć książkęKup książkę TWORZENIE PYTHONICZNEGO KODU Tylko tak: def calculate_interest(principle, time, rate): if principle 0: return (principle * time * rate) / 100 else: return None def calculate_interest(principle, time, rate): if principle 0: return None return (principle * time * rate) / 100 Stosuj metody startswith() i endswith() W instrukcjach sprawdzających prefiksy i sufiksy ciągów stosuj metody startswith() i endswith() zamiast wyrażeń wyodrębniających fragmenty ciągu. Instrukcja wyodrębniania fragmentu ciągu jest bardzo przydatna, szczególnie w przypadku przetwarzania dłuższych ciągów lub wykonywania na nich dodatkowych operacji. Jeżeli trzeba po prostu sprawdzić prefiks lub sufiks, lepiej stosować metody startswith() i endswith(), ponieważ kod jest wtedy bardziej zrozumiały. Nie rób tak: data = Witaj, co słychać? if data[:5] == Witaj : Tylko tak: data = Witaj, co słychać? if data.startswith( Witaj ): Sprawdzaj typy za pomocą metody isinstance(), a nie type() Do sprawdzania typu danych używaj metody isinstance(), a nie type(), ponieważ tę pierwszą można stosować również w przypadku podklas. Przeanalizujmy przypadek struktury będącej podklasą dict, na przykład orderdict. Metoda type() nie będzie działać poprawnie, natomiast isinstance() prawidłowo rozpozna, że struktura jest podklasą dict. Nie rób tak: user_ages = { Leszek : 35, Joanna : 89, Igor : 12} if type(user_ages) == dict: Tylko tak: user_ages = { Leszek : 35, Joanna : 89, Igor : 12} if isinstance(user_ages, dict): Porównywanie wartości logicznych Wartości logiczne można porównywać na kilka sposobów. 21 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Nie rób tak: is_empty = False if is_empty == False: if is_empty is False: Tylko tak: is_empty = False if not is_empty: Pisz ścisły kod dla menedżera kontekstu Jeżeli używasz instrukcji with, twórz osobne funkcje wykonujące operacje początkowe, główne i końcowe. Nie rób tak: class NewProtocol: def __init__(self, host, port, data): self.host = host self.port = port self.data = data def __enter__(self): self._client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._client.connect((self.host, self.port)) self._transfer_data() def __exit__(self, exception, value, traceback): self._receive_data() self._client.close() def _transfer_data(self): ... def _receive_data(self): ... con = NewProtocol(host, port, data) Tylko tak: class NewProtocol: def __init__(self, host, port): self.host = host self.port = port def __enter__(self): self._client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._client.connect((self.host, self.port)) def __exit__(self, exception, value, traceback): self._client.close() def transfer_data(self, payload): ... def receive_data(self): ... with NewProtocol(host, port) as con: transfer_data() receive_data() 22 Poleć książkęKup książkę TWORZENIE PYTHONICZNEGO KODU Metody __enter__() i __exit__() w pierwszej wersji oprócz otwierania i zamykania połączenia wykonują również dodatkowe operacje. Lepszym rozwiązaniem jest tworzenie dla tych operacji osobnych metod. Stosuj narzędzia weryfikujące poprawność kodu Narzędzia weryfikujące poprawność kodu ułatwiają utrzymanie go w spójnym formacie, co jest bardzo ważne, szczególnie w skali całego projektu. Narzędzia te analizują kod pod kątem następujących cech:  błędów składniowych;  niewykorzystywanych zmiennych i niewłaściwych argumentów funkcji;  niezgodności z zaleceniami PEP8. Dzięki narzędziom weryfikującym poprawność kodowanie jest znacznie wydajniejsze, ponieważ na bieżąco wyszukiwane są różne nieścisłości. Dla języka Python dostępnych jest wiele takich narzędzi. Niektóre z nich obejmują określone obszary, na przykład style komentarzy dokumentacyjnych. Narzędzia takie jak Flake8 lub Pylint weryfikują kod pod kątem wszystkich zaleceń PEP8, inne — tylko wybranych. Na przykład mypy sprawdza typy danych. Możesz używać wszystkich lub tylko wybranych, przeprowadzających standardową weryfikację pod kątem zaleceń PEP8. Najlepsze narzędzia to Flake8 i Pylint. Zawsze upewniaj się, czy narzędzie, którego używasz, weryfikuje kod pod kątem zaleceń PEP8. Narzędzie weryfikujące powinno sprawdzać następujące aspekty kodu:  zgodność z zaleceniami PEP8;  kolejność importowanych modułów;  konwencje nazw zmiennych, funkcji, klas, modułów, plików itp.;  zapętlone instrukcje importujące;  złożoność (liczba wierszy kodu funkcji, pętli i innych struktur);  poprawność pisowni;  styl komentarzy dokumentacyjnych. Narzędzia weryfikujące stosuje się na różne sposoby, m.in.:  podczas kodowania w środowisku IDE;  podczas zatwierdzania kodu;  w systemach ciągłej integracji, np. Jenkins lub CircleCI.  Uwaga Istnieją różne dobre praktyki, dzięki którym zdecydowanie można podnieść jakość kodu. Jeżeli chcesz je jak najpełniej wykorzystać, zapoznaj się z oficjalną dokumentacją PEP8. Pomocne jest również przeglądanie dobrego kodu umieszczonego w serwisie GitHub. 23 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Komentarze dokumentacyjne Komentarze doskonale nadają się do tworzenia dokumentacji kodu w języku Python. Komentarz dokumentacyjny umieszcza się zazwyczaj na początku kodu metody, kolumny lub modułu. Jest on specjalnym atrybutem __doc__ danego obiektu. Zgodnie z PEP8 komentarz dokumentacyjny umieszcza się wewnątrz potrójnych cudzysłowów (patrz listing 1.14). Poznaj kilka dobrych praktyk umieszczania komentarzy w kodzie. Listing 1.14. Funkcja z komentarzem dokumentacyjnym def get_prime_number(): Lista liczb pierwszych z zakresu od 1 do 100. Komentarze dokumentacyjne można wpisywać na różne sposoby (opisane w dalszej części rozdziału), jednak zgodnie z kilkoma wymienionymi niżej zasadami:  Potrójny cudzysłów stosuje się nawet wtedy, gdy komentarz zajmuje tylko jeden wiersz. Jest to dobra praktyka umożliwiająca rozszerzanie komentarza.  Przed komentarzem i po nim nie pozostawia się pustych wierszy.  Na końcu komentarza umieszcza się kropkę. Podobne zasady obowiązują podczas umieszczania wielowyrazowych komentarzy dokładniej opisujących kod (patrz listing 1.15). Dzięki temu dokumentacja kodu jest umieszczana w nim samym i nie trzeba korzystać z osobnych, długich i nużących opisów. Listing 1.15. Wielowierszowy komentarz dokumentacyjny def call_weather_api(url, location): Informacje o pogodzie w zadanym miejscu. Funkcja weather_api() zwraca informacje o pogodzie w zadanym miejscu, wykorzystując zadany interfejs API. W argumencie location można umieszczać jedynie nazwy miast. Umieszczenie nazwy państwa lub regionu spowoduje zgłoszenie wyjątku. :param url: Adres interfejsu API z informacjami o pogodzie. :type url: str :param location: Nazwa miasta. :type location: str :return: Informacje o pogodzie w zadanym mieście. :rtype: str Zwróć uwagę na kilka następujących szczegółów:  Pierwszy wiersz zawiera krótki opis funkcji lub klasy.  Na końcu wiersza jest kropka.  Pomiędzy krótkim a pełnym opisem znajduje się pusty wiersz. 24 Poleć książkęKup książkę KOMENTARZE DOKUMENTACYJNE Ten sam kod napisany w języku Python 3 z modułem typing wyglądałby jak w listingu 1.16. Listing 1.16. Wielowierszowy komentarz dokumentacyjny i moduł typing def call_weather_api(url: str, location: str) - str: Informacje o pogodzie w zadanym miejscu. Funkcja weather_api() zwraca informacje o pogodzie w zadanym miejscu, wykorzystując zadany interfejs API. W argumencie location można umieszczać jedynie nazwy miast. Umieszczenie nazwy państwa lub regionu spowoduje zgłoszenie wyjątku. Jeżeli w kodzie stosujesz nazwy typów, nie musisz w komentarzu opisywać argumentów. Jak wspomniałem wcześniej, na przestrzeni lat wypracowano kilka stylów komentarzy dokumentacyjnych, z których żaden nie jest bardziej zalecany lub lepszy od innych. Ważne jest zatem, aby wybrany styl stosować konsekwentnie, dzięki czemu dokumentacja będzie miała spójny format. Komentarze dokumentacyjne można wpisywać na kilka sposobów.  Stosowany przez Google: Wysłanie żądania na określony adres URL. Parameters: url (str): Adres URL, na który wysyłane jest żądanie. Returns: dict: Odpowiedź zwrócona przez interfejs API.  Strukturalny, zalecany w oficjalnej dokumentacji: Wysłanie żądania na określony adres URL. :param url: Adres URL, na który wysyłane jest żądanie. :type url: str :returns: Odpowiedź zwrócona przez interfejs API. :rtype: dict  Przyjęty w narzędziach NumPy i SciPy: Wysłanie żądania na określony adres URL. Parameters ---------- url : str Adres URL, na który wysyłane jest żądanie. Returns ------- dict Odpowiedź zwrócona przez interfejs API. 25 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE  Przyjęty w narzędziu Epytext: Wysłanie żądania na określony adres URL. @type url: str @param file_loc: Adres URL, na który wysyłane jest żądanie. @rtype: dict @returns: Odpowiedź zwrócona przez interfejs API. Komentarze dokumentacyjne do modułów Komentarz dokumentacyjny opisujący zastosowanie modułu umieszcza się na początku pliku, przed instrukcjami import (patrz listing 1.17). Komentarz powinien zawierać przeznaczenie modułu i zawartych w nim wszystkich klas i metod. Nie powinien skupiać się na określonych klasach i metodach. Niektóre z nich można krótko opisać, jeżeli użytkownik powinien się czegoś o nich dowiedzieć przez użyciem modułu. Listing 1.17. Modułowy komentarz dokumentacyjny Moduł zawiera wszystkie potrzebne wywołania sieciowe. Kontroluje wszystkie zgłaszane przez wywołania wyjątki i zgłasza własne w sytuacjach nieprzewidzianych. Kod wykorzystujący ten moduł musi obsługiwać następujące wyjątki: NetworkError: błąd sieciowy. NetworkNotFound: nieznany adres sieciowy. import urllib3 import json ... Podczas wpisywania modułowych komentarzy dokumentacyjnych pamiętaj o następujących zasadach:  Krótko opisuj przeznaczenie modułu.  Możesz umieszczać dokładniejsze informacje, na przykład o wyjątkach, jak w listingu 1.17, jeżeli uznasz, że są one ważne dla użytkownika. Nie wchodź jednak zbyt głęboko w szczegóły.  Komentarz powinien zawierać ogólne informacje o module, bez szczegółowego opisywania każdej zawartej w nim funkcji lub operacji wykonywanych przez klasy. Komentarze dokumentacyjne do klas Komentarz dokumentacyjny zazwyczaj krótko opisuje przeznaczenie i sposób użycia klasy (patrz listing 1.18). Przeanalizujmy kilka przykładów. 26 Poleć książkęKup książkę KOMENTARZE DOKUMENTACYJNE Listing 1.18. Jednowierszowy komentarz dokumentacyjny class Student: Klasa implementująca operacje wykonywane przez studenta. def __init__(self): pass Powyższy jednowierszowy komentarz krótko opisuje klasę Student. Tworząc tego rodzaju komentarze, przestrzegaj opisanych wcześniej zasad. Przeanalizujmy wielowierszowy komentarz do klasy przedstawiony w listingu 1.19. Listing 1.19. Wielowierszowy komentarz do klasy class Student: Informacje o klasie Student. Klasa implementuje operacje wykonywane przez studenta. Zawiera m.in. informacje o nazwisku, wieku i ocenach. Użycie import student student = student.Student() student.get_name() 678998 def __init__(self): pass Powyższy wielowierszowy komentarz zawiera nieco więcej informacji o przeznaczeniu klasy i sposobie jej użycia. Komentarze dokumentacyjne do funkcji Komentarz dokumentacyjny umieszcza się na początku funkcji. Zazwyczaj opisuje on jej działanie. Jeżeli nie używasz modułu typing, opisuj w komentarzu również argumenty funkcji. Listing 1.20 przedstawia przykładowy komentarz. Listing 1.20. Komentarz dokumentacyjny do funkcji def is_prime_number(number): Sprawdzenie, czy liczba jest pierwsza. Funkcja sprawdza, czy wśród kolejnych liczb mniejszych od pierwiastka kwadratowego zadanej liczby znajduje się liczba pierwsza. :param number: Sprawdzana liczba. :type number: int :return: True, jeżeli liczba jest pierwsza, lub False w przeciwnym wypadku. :rtype: boolean ... 27 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Przydatne narzędzia dokumentacyjne Dostępnych jest bardzo dużo narzędzi tworzących na podstawie komentarzy dokumentację kodu w formacie HTML. Aby zaktualizować dokumentację, nie trzeba jej ręcznie zmieniać. Wystarczy jedynie użyć prostego polecenia. Narzędzia te stosowane w procesie tworzenia oprogramowania w dłuższym okresie są nieocenione. Każde z nich ma inne przeznaczenie.  Sphinx (http://www.sphinx-doc.org/en/stable) — najpopularniejsze narzędzie automatycznie generujące dokumentację kodu w różnych formatach.  Pycco (https://pycco-docs.github.io/pycco) — narzędzie do szybkiego generowania dokumentacji. Jego największą zaletą jest możliwość wyświetlania dokumentacji obok kodu.  Read the Docs (https://readthedocs.org) — popularne, bezpłatne narzędzie umożliwiające tworzenie, wersjonowanie i przechowywanie dokumentacji.  Epydocs (http://epydoc.sourceforge.net) — narzędzie do tworzenia dokumentacji modułów. Dzięki powyższym narzędziom łatwiej jest w dłuższym okresie utrzymywać kod i dokumentację w spójnym formacie.  Uwaga Komentarze dokumentacyjne są doskonałą funkcjonalnością języka Python. Dzięki nim tworzenie dokumentacji kodu jest naprawdę proste, więc stosuj je jak najczęściej. Zaoszczędzisz wtedy mnóstwo czasu, szczególnie gdy projekt się rozwinie i będzie zawierał kilka tysięcy wierszy kodu. Pythoniczne struktury sterujące Struktury sterujące stanowią fundamentalną część każdego języka programowania. Dotyczy to również Pythona, w którym struktury można kodować na różne sposoby. W tej części rozdziału opisano kilka dobrych praktyk, dzięki którym kod jest bardziej czytelny. Wyrażenia listowe Wyrażenie listowe funkcjonuje podobnie jak pętla for. Można w nim stosować instrukcję if. W Pythonie istnieje kilka sposobów tworzenia listy na podstawie innej listy. Służą do tego celu głównie funkcje filter() i map(). Zalecane jest jednak stosowanie wyrażeń listowych, ponieważ dzięki nim kod jest o wiele bardziej czytelny. 28 Poleć książkęKup książkę PYTHONICZNE STRUKTURY STERUJĄCE Poniższy kod wylicza kwadraty liczb za pomocą funkcji map(): numbers = [10, 45, 34, 89, 34, 23, 6] square_numbers = map(lambda num: num**2, numbers) Wersja wykorzystująca wyrażenie listowe wygląda tak: square_numbers = [num**2 for num in numbers] Przeanalizujmy inny przykład kodu filtrującego wartości True. Poniżej przedstawiona jest wersja wykorzystująca funkcję filter(): data = [1, A , 0, False, True] filtered_data = filter(None, data) Wersja wykorzystująca wyrażenie listowe wygląda następująco: filtered_data = [item for item in data if item] Jak widać, kod z wyrażeniami listowymi jest bardziej czytelny niż z funkcjami filter() i map(). Również oficjalna dokumentacja do Pythona zaleca stosowanie tych wyrażeń. Jeżeli nie używasz skomplikowanych warunków lub pętli, stosuj wyrażenia listowe. Jednak jeżeli w wyrażeniu ma być wykonywanych kilka operacji, wtedy ze względu na czytelność kodu lepiej pozostać przy pętli. Aby zilustrować przewagę wyrażenia nad pętlą, przeanalizujmy przykład kodu wyszukującego samogłoski w liście liter: list_char = [ a , p , t , i , y , l ] vowel = [ a , e , i , o , u , y ] only_vowel = [] for item in list_char: if item in vowel: only_vowel.append(item) Poniższa wersja wykorzystuje wyrażenie listowe: only_vowel = [item for item in list_char if item in vowel] Jak widać, wyrażenie listowe jest bardziej czytelne niż pętla i zajmuje mniej wierszy kodu. Ponadto jest bardziej wydajne, ponieważ nie jest w nim za każdym razem dopisywana wartość do listy, jak to ma miejsce w przypadku pętli. Wyrażenie jest również bardziej wydajne niż funkcje filter() i map(). Nie twórz skomplikowanych wyrażeń listowych Wyrażenia listowe nie powinny być nadmiernie skomplikowane, ponieważ kod jest wtedy mniej czytelny i bardziej podatny na błędy. Jedno wyrażenie powinno zastępować najwyżej dwie pętle. Jeżeli będzie ich więcej, czytelność kodu pogorszy się. 29 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Przeanalizujmy inny przykład użycia wyrażenia listowego. Zadanie polega na przetransponowaniu następującej tablicy: matrix = [[1,2,3], [4,5,6], [7,8,9]] Wynikiem ma być poniższa tablica: [[1,4,7], [2,5,8], [3,6,9]] Wyrażenie listowe transponujące tablicę ma następującą postać: return [[ matrix[row][col] for row in range(0, 3) ] for col in range(0, 3) ] Powyższe wyrażenie jest czytelne, więc użycie go jest uzasadnione. Można je nawet zapisać w następującej postaci: return [[ matrix[row][col] for row in range(0, 3) ] for col in range(0, 3) ] Pętle zamiast wyrażeń listowych stosuj wtedy, gdy warunków jest kilka, jak niżej: ages = [1, 34, 5, 7, 3, 57, 356] old = [age for age in ages if age 10 and age 100 and age is not None] W drugim wierszu dużo się dzieje, przez co kod jest mniej czytelny i podatny na błędy. Dlatego lepszym rozwiązaniem jest zastosowanie pętli. Kod może wyglądać na przykład tak: ages = [1, 34, 5, 7, 3, 57, 356] old = [] for age in ages: if age 10 and age 100: old.append(age) W tej wersji kod składa się z większej liczby wierszy, ale jest bardziej czytelny. Niepisaną dobrą praktyką jest tworzenie najpierw wyrażeń listowych, a następnie zamienianie ich na pętle, jeżeli wyrażenia okażą się zbyt skomplikowane i nieczytelne.  Uwaga Rozważne stosowanie wyrażeń listowych bardzo poprawia jakość kodu. Jednak jeżeli używa się ich nadmiernie, kod przestaje być czytelny. Dlatego lepiej jest z nich zrezygnować, jeżeli robią się skomplikowane, np. zawierają więcej niż dwa warunki lub dwie pętle. 30 Poleć książkęKup książkę PYTHONICZNE STRUKTURY STERUJĄCE Kiedy stosować wyrażenia lambda? Wyrażenia lambda warto stosować zamiast funkcji wtedy, gdy dzięki nim kod staje się bardziej czytelny. Przeanalizujmy przykład pokazany w listingu 1.21. Listing 1.21. Wyrażenie lambda bez instrukcji przypisania data = [[7], [3], [0], [8], [1], [4]] def min_val(data): Wyszukiwanie minimalnej wartości w liście. return min(data, key=lambda x: len(x)) W powyższym kodzie wyrażenie lambda zostało użyte w charakterze doraźnej funkcji wyszukującej minimalną wartość w liście. Jednak lepiej nie stosować wyrażenia lambda jako anonimowej funkcji, jak niżej: min_val = min(data, key=lambda x: len(x)) Tutaj wartość zmiennej min_val jest wyliczana za pomocą wyrażenia lambda. Stosowanie wyrażenia lambda zamiast funkcji jest powieleniem instrukcji def, co nie jest zgodne z filozofią Pythona, która zaleca, aby każdą rzecz robić tylko w jeden sposób. Dokumentacja PEP8 tak mówi na ten temat: W instrukcjach przypisujących wartości bezpośrednio do zmiennych zawsze stosuj funkcje, a nie wyrażenia lambda. Rób tak: def f(x): return 2*x A nie tak: f = lambda x: 2*x W pierwszym przypadku obiekt ma konkretną nazwę f, a nie ogólną lambda. Dzięki temu komunikaty o błędach są bardziej czytelne, jak również łatwiej uzyskać tekstową reprezentację obiektu. W instrukcji przypisania nie jest wykorzystywana najważniejsza cecha wyrażenia lambda, tj. możliwość osadzania go w większych wyrażeniach. Kiedy stosować generatory, a kiedy wyrażenia listowe? Wyrażenie listowe różni się od generatora przede wszystkim tym, że zajmuje pamięć. Dlatego wyrażenia należy stosować w następujących sytuacjach:  gdy lista wartości jest iterowana wielokrotnie,  gdy dane są przetwarzane za pomocą metod niedostępnych w generatorach,  gdy danych nie jest dużo i umieszczenie ich w pamięci nie będzie przyczyną problemów. 31 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Przeanalizujmy kod przedstawiony w listingu 1.22, wyszukujący w pliku określone wiersze. Listing 1.22. Odczytywanie wierszy z pliku za pomocą wyrażenia listowego def read_file(file_name): Odczytywanie pliku wiersz po wierszu. fread = open(file_name, r ) data = [line for line in fread if line.startswith( )] return data print(read_file( log.txt )) Jeżeli plik jest duży, wtedy umieszczenie wierszy w liście może skutkować zajęciem dużej ilości pamięci i spowolnieniem działania kodu. Dlatego w takim przypadku lepiej jest stosować generator, jak w listingu 1.23. Listing 1.23. Odczytywanie wierszy z pliku za pomocą iteratora def read_file(file_name): Odczytywanie pliku wiersz po wierszu. with open(file_name) as fread: for line in fread: yield line for line in read_file( log.txt ): if line.startswith( ): print(line) W powyższym listingu dane nie są umieszczane w pamięci w formie wyrażenia listowego, tylko odczytywane i przetwarzane wiersz po wierszu. Wyrażenie jednak można wykorzystywać wielokrotnie, natomiast generator trzeba uruchamiać za każdym razem, gdy zaistnieje konieczność przetworzenia wierszy zaczynających się od znaków . Obie funkcjonalności są jednak bardzo ważne i stosując je zgodnie z powyższym opisem, można tworzyć wydajny kod. Dlaczego nie należy stosować instrukcji else w pętlach? W Pythonie instrukcję else można stosować w pętlach for i while. Kod umieszczony w bloku else jest wykonywany po zakończeniu działania pętli. Jeżeli pętla zostanie przerwana za pomocą instrukcji break, wtedy blok else nie jest wykonywany. Instrukcja else użyta w pętli może wprowadzać zamieszanie w kodzie, dlatego wielu programistów jej nie stosuje. Jest to zrozumiałe, gdy porówna się działanie tej instrukcji w zwykłej strukturze if/else. Przeanalizujmy prosty przykład przedstawiony w listingu 1.24. Jest to pętla przetwarzająca listę, zawierająca instrukcję else. 32 Poleć książkęKup książkę PYTHONICZNE STRUKTURY STERUJĄCE Listing 1.24. Pętla z instrukcją else for item in [1, 2, 3]: print( Iteracja ) else: print( Koniec ) Wynik: Iteracja Iteracja Iteracja Koniec Wydawałoby się, że blok else powinien być pominięty, a wynik powinien zawierać tylko słowa Iteracja. Tak nakazuje logika właściwa dla struktury if/else. Jednakże w przypadku pętli jest inaczej. Rzecz się komplikuje jeszcze bardziej w pętli while, jak w listingu 1.25. Listing 1.25. Pętla while z instrukcją else x = [1, 2, 3] while x: print( Iteracja ) x.pop() else: print( Koniec ) Wynik działania kodu jest następujący: Iteracja Iteracja Iteracja Koniec W tym przypadku pętla jest wykonywana do chwili opróżnienia listy, po czym jest wykonywany blok else. Opisywana funkcjonalność ma swoje uzasadnienie. Jednym z jej zastosowań jest implementacja dodatkowych operacji wykonywanych tylko po normalnym zakończeniu działania pętli (patrz listing 1.26). Listing 1.26. Pętla z instrukcjami break i else for item in [1, 2, 3]: if item 2 == 0: break print( Iteracja ) else: print( Koniec ) 33 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Wynik jest następujący: Iteracja Są jednak lepsze sposoby kodowania niż implementacja pętli z instrukcją break w środku (lub bez niej) oraz else na końcu. Ten sam efekt można osiągnąć bez instrukcji else. Należy użyć instrukcji if (patrz listing 1.27). Kod na pierwszy rzut oka będzie mniej czytelny, ale ryzyko niezrozumienia go przez innego programistę jest mniejsze. Listing 1.27. Pętla z instrukcją break flag = True for item in [1, 2, 3]: if item 2 == 0: flag = False break print( Iteracja ) if flag: print( Koniec ) Wynik działania kodu jest następujący: Koniec Powyższy kod jest łatwiej zrozumieć i nie ma możliwości jego błędnej interpretacji. Instrukcja else jest ciekawą funkcjonalnością języka, jednak może pogarszać czytelność kodu. Dlatego lepiej unikać jej stosowania. Dlaczego warto stosować funkcję range() w języku Python 3? W języku Python 2 na pewno stosowałeś funkcję xrange(). W wersji 3 pojawiła się funkcja range(), podobna do swojej poprzedniczki, ale iterowalna i oferująca kilka dodatkowych możliwości. range(4) range(0, 5) # Iterowalny zakres list(range(4)) [0, 1, 2, 3, 4] # Lista Przewaga funkcji range() nad listą polega przede wszystkim na tym, że dane nie są umieszczane w pamięci. W odróżnieniu od list, krotek i innych struktur, funkcja range() reprezentuje niemutowalny, iterowalny obiekt, zajmujący niewielką ilość pamięci niezależnie od rozmiaru danych. Przechowywane są jedynie wartości początkowa, końcowa i przyrostowa. Dane są zawsze generowane na żądanie. 34 Poleć książkęKup książkę Za pomocą funkcji range() można wykonywać kilka opisanych niżej operacji ZGŁASZANIE WYJĄTKÓW nieosiągalnych za pomocą xrange().  Porównywanie zakresów danych: range(4) == range(4) True range(4) == range(5) False  Wyodrębnianie fragmentów danych: range(10)[2:] range(2, 10) range(10)[2:7, -1) range(2, 7, -1) Więcej informacji o funkcji range() znajdziesz na stronie https://docs.python.org/3.0/library/functions.html#range. Jeżeli trzeba wykonywać operacje na liczbach, lepiej jest stosować funkcję range(), a nie listę, ponieważ funkcja ta jest wydajniejsza, jak również łatwiej ją stosować w pętli. Nie rób tak: for item in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]: print(item) Tylko tak: for item in range(10): print(item) Wydajność pierwszej pętli jest znacznie mniejsza niż drugiej, ponieważ każda wartość musi być odczytana z pamięci. Jeżeli zakres wartości jest duży, wtedy kod będzie działał znacznie wolniej. Zgłaszanie wyjątków Wyjątki są symptomami błędów w kodzie. W Pythonie wyjątki obsługuje wbudowany moduł. Dogłębna znajomość wyjątków, miejsc i okoliczności, w których są zgłaszane, pozwala tworzyć kod mniej podatny na błędy. Za pomocą wyjątków można łatwo sygnalizować błędy w kodzie, dlatego warto z nich korzystać. Pozwalają poznać ograniczenia wykorzystywanego interfejsu API lub biblioteki. Zgłaszane w odpowiednim miejscu ułatwiają innym programistom korzystanie z kodu lub interfejsu API. 35 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Często zgłaszane wyjątki Zapewne zastanawiasz się, gdzie i kiedy należy zgłaszać wyjątki. Według mnie trzeba to robić wtedy, gdy nie są spełniane podstawowe założenia przyjęte w danym bloku kodu. Preferowane jest zgłaszanie wyjątków w przypadku pojawienia się poważnego błędu. Należy je zgłaszać nawet wtedy, gdy pojawia się permanentny problem. Przeanalizujmy listing 1.28, w którym jedna liczba jest dzielona przez inną. Listing 1.28. Dzielenie liczb i zgłaszanie wyjątku def division(dividend, divisor): Dzielenie dwóch liczb. try: return dividend/divisor except ZeroDivisionError as zero: raise ZeroDivisionError( Dzielnik musi być różny od zera ) Jak pokazuje powyższy kod, wyjątki należy zgłaszać w tych miejscach kodu, w których pojawiają się błędy. Dzięki temu w przypadku wystąpienia problemu zewnętrzny kod będzie mógł odpowiednio zareagować (patrz listing 1.29). Listing 1.29. Dzielenie liczb bez zgłaszania wyjątku result = division(10, 2) Co się stanie, gdy funkcja nie zgłosi wyjątku, tylko zwróci wynik None? def division(dividend, divisor): Dzielenie dwóch liczb. try: return dividend/divisor except ZeroDivisionError as zero: return None Powyższa funkcja w przypadku wystąpienia wyjątku ZeroDivisionError nie zgłosi problemu, tylko zwróci wynik None. Z tego powodu trudniej będzie diagnozować wywołujący ją kod, szczególnie jeżeli będzie on rozbudowany lub zmienią się stawiane mu wymagania. Funkcja nie powinna zwracać wyniku None, ponieważ w wywołującym ją kodzie nie będzie można określić przyczyny problemu. Jeżeli natomiast będzie zgłaszany wyjątek, wtedy w kodzie wywołującym funkcję będzie od razu wiadomo, że wartości argumentów są błędne i trzeba poprawić ukryte błędy. W zewnętrznym kodzie łatwiej obsługiwać wyjątki niż wyniki funkcji sygnalizujące problem. Zasada obowiązująca w Pythonie brzmi: lepiej prosić o wybaczenie niż o pozwolenie. Oznacza to, że nie należy sprawdzać zawczasu, czy pojawi się błąd, tylko obsługiwać wyjątek, gdy zostanie już zgłoszony. Zatem należy w kodzie zgłaszać wyjątki wszędzie tam, gdzie mogą występować błędy. Zewnętrzny kod nie będzie wtedy „zaskakiwany” i będzie mógł odpowiednio reagować. 36 Poleć książkęKup książkę ZGŁASZANIE WYJĄTKÓW Obsługuj wyjątki za pomocą instrukcji finally Kod w bloku finally jest wykonywany niezależnie od tego, czy został zgłoszony wyjątek, czy nie. Instrukcja ta przydaje się szczególnie w sytuacjach, gdy kod wykorzystuje zewnętrzne zasoby. Dzięki niej można np. zamykać pliki i zwalniać zasoby. Przeanalizujmy kod w listingu 1.30. Listing 1.30. Przykład użycia instrukcji finally def send_email(host, port, user, password, email, message): Wysłanie wiadomości e-mail na zadany adres. try: server = smtlib.SMTP(host=host, port=port) server.ehlo() server.login(user, password) server.send_email(message) finally: server.quit() W tej sytuacji w bloku finally w przypadku zgłoszenia wyjątku przez metodę login() lub send_mail() zwalniane są zasoby zajmowane podczas nawiązywania połączenia z serwerem. W bloku finally można zamykać otwarte pliki, jak w listingu 1.31. Listing 1.31. Zamykanie plików w bloku finally def write_file(file_name): Zapisanie wiersza w pliku. myfile = open(file_name, w ) try: myfile.write( Python jest świetny! ) # Możliwy wyjątek. finally: myfile.close() # Instrukcja wykonywana przed eskalacją błędu. Jeżeli więc jakiś kod ma być wykonywany niezależnie od tego, czy zostanie zgłoszony wyjątek, czy nie, należy go umieszczać w bloku finally. Dzięki temu zasoby będą właściwie wykorzystywane, a kod bardziej czytelny. Twórz własne klasy wyjątków Tworząc interfejs API lub bibliotekę, warto definiować własne klasy wyjątków, dzięki którym łatwiej będzie diagnozować przyczyny problemów. Załóżmy, że wyjątek ma być zgłaszany w przypadku, gdy w bazie danych nie zostaną znalezione dane o użytkowniku. Klasa wyjątku musi opisywać problem. Nazwa wyjątku, np. UserNotFoundError, informuje o przyczynie zgłoszenia wyjątku. 37 Poleć książkęKup książkę ROZDZIAŁ 1.  KODOWANIE PYTHONICZNE Listing 1.32 przedstawia przykład klasy wyjątku zaimplementowanej w języku Python 3. Listing 1.32. Własna klasa wyjątku class UserNotFoundError(Exception): Zgłoszenie wyjątku, gdy użytkownik nie zostanie znaleziony. def __init__(self, message=None, errors=None): # Wywołanie konstruktora klasy bazowej z wymaganymi argumentami. super().__init__(message) self.errors = errors def get_user_info(user_obj): Odczytanie danych użytkownika z bazy. user = get_user_from_db(user_obj) if not user: raise UserNot
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Czysty kod w Pythonie
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: