Jak Zen Pythona może pomóc w pisaniu lepszego kodu

Chcesz lepiej pisać kod w Pythonie? Oto jak Zen Pythona może pomóc Ci zrobić pierwsze kroki w tym kierunku.

Python jest bardzo prosty do nauczenia. Ale pisanie kodu idiomatycznego i Pythona, który jest łatwy w utrzymaniu, może być wyzwaniem — szczególnie dla początkujących programistów. PEP-20 przedstawił „The Zen of Python”, wiersz Tima Petersa, który podkreśla znaczenie pisania kodu w języku Python, który jest zgodny z najlepszymi praktykami.

Aby przeczytać Zen Pythona, możesz uruchomić Python REPL i uruchomić:

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Jak widać, większość aforyzmów w Zen Pythona jest oczywista. Podczas interpretacji niektóre aforyzmy należy łączyć z następnymi, podczas gdy inne zaprzeczają wcześniejszemu aforyzmowi. Niemniej jednak Zen of Python to zabawna, wciągająca i praktyczna lektura!

Interpretacja Zen Pythona

Zaproponowano, aby Zen of Python miał 20 przewodnich zasad programowania w Pythonie. Jednak do tej pory jest tylko 19 aforyzmów. Przejrzyjmy je.

Piękne jest lepsze niż brzydkie.

Ten aforyzm podkreśla znaczenie pisania eleganckiego i Pythonowego kodu.

Poniższy fragment kodu ma zapach kodu:

def square(num):
    squares = []
    for i in range(num):
        squares.append(i*i)
    return squares

Funkcja:

  • Inicjuje pustą listę
  • Ma pętlę wewnątrz funkcji, która dołącza elementy na końcu listy i
  • Na koniec zwraca listę

Chociaż jest to poprawne funkcjonalnie — nie jest Pythonic — i jest trudne w utrzymaniu.

Możesz napisać to znacznie bardziej elegancko, używając generatorów. Oto odpowiednik funkcji generatora powyższej funkcji:

def square(num):
    for i in range(num):
        yield i*i

Lub jeszcze lepiej, możesz mieć następujące wyrażenie generatora zrozumienia:

num = ...
squares = (i*i for i in range(num))

Jawne jest lepsze niż ukryte.

Pisząc kod, nie pozwól innym programistom i użytkownikom zgadywać domniemanego lub domyślnego zachowania kodu. Bądź wyraźny. Weźmy przykład importu symboli wieloznacznych:

from some_module import * # wildcard import
from some_other_module import *

result = some_function() # where did this come from?

Unikaj używania symboli wieloznacznych w jak największym stopniu. Bo to nie jest jednoznaczne i nieefektywne. Bądź konkretny podczas importowania funkcji i klas z innych modułów:

from some_module import this_function # explicit import

result = this_function() # we now know.

Proste jest lepsze niż złożone.

Ten aforyzm mówi, że powinniśmy zachować prostotę kodu i unikać niepotrzebnej złożoności. Na przykład: możesz chcieć odwrócić ciąg i zaimplementować następujące rozwiązanie rekurencyjne:

def reverse_string(my_string):
  if my_string == "":
    return my_string
  else:
    return reverse_string(my_string[1:]) + my_string[:1]

Chociaż to działa, jest to prawdopodobnie przekombinowane rozwiązanie tego problemu — biorąc pod uwagę, że istnieją prostsze i bardziej Pythoniczne sposoby na zrobienie tego.

Oto podejście polegające na krojeniu ciągów:

>>> rev_string = my_string[::-1]
>>> rev_string
'nohtyP'

A oto podejście wykorzystujące wbudowane metody i funkcje:

>>> rev_string = ''.join(reversed(my_string))
>>> rev_string
'nohtyP'

Złożone jest lepsze niż skomplikowane.

Co więc przekazuje ten następny aforyzm w Zen Pythona?

Odwrócenie łańcucha w Pythonie to bardzo prosta operacja. W praktyce jednak możemy potrzebować bardziej złożonej logiki. Oto dość prosty przykład:

Powiedzmy, że musisz połączyć się z bazą danych:

  • Najpierw należy przeanalizować plik konfiguracyjny toml — aby pobrać informacje o konfiguracji bazy danych.
  • Łącznik bazy danych powinien być zainstalowany.
  • Następnie możesz zdefiniować funkcję, która połączy się z bazą danych, przewidzieć błędy połączenia, zaimplementować obsługę błędów i wiele więcej.
  • Wreszcie, po połączeniu się z bazą danych, możesz ją zapytać.

Chociaż jest to nadal dość proste, wymaga bardziej złożonej logiki w porównaniu do odwrócenia łańcucha. Ale to nie znaczy, że musi być skomplikowane. Nadal możesz efektywnie korzystać z funkcji wbudowanego kodu modułów i organizować swój kod, aby inni programiści mogli go czytać, rozumieć i współtworzyć.

Mieszkanie jest lepsze niż zagnieżdżone.

Struktura płaska jest łatwiejsza do przeanalizowania i zrozumienia niż struktura zagnieżdżona. Podczas pracy nad projektem możesz ulec pokusie izolowania funkcjonalności poprzez tworzenie osobnych modułów. Jednak zbyt duża ziarnistość może być nadmierna.

To powiedziawszy, często może być konieczne wyjście poza płaską strukturę. Ale nawet jeśli potrzebujesz zagnieżdżenia, ogranicz je do minimum.

Oto przykład:

from db_info.config.actions.parse.parse_config import parse_toml # too difficult to parse!
...

from db_config.parse_config import parse_toml # much better!
...

Rzadki jest lepszy niż gęsty.

Jeśli dopiero zaczynasz swoją przygodę z programowaniem, możesz ulec pokusie nadużywania niektórych funkcji tego języka. Na przykład wyrażenia listowe są w Pythonie — ale tylko wtedy, gdy używasz ich tam, gdzie są potrzebne.

Spójrz na następujące rozumowanie:

prices_dict = {'melons':40,'apples':70,'berries':55}
items = [(fruit,price) for fruit in prices_dict.keys() if fruit.startswith('m') for price in prices_dict.values() if price < 50]
print(items)
# Output: [('melons', 40)]

Lista składana jest zbyt gęsta i trudna do przeanalizowania. W takim przypadku użycie odpowiednika pętli for z wyrażeniami warunkowymi będzie bardziej czytelne. Znaczenie zrozumienia jest trudne do zrozumienia. 🙂

Liczy się czytelność.

Zawsze powinieneś pisać czytelny kod. Oto kilka prostych sposobów na poprawę czytelności kodu:

  • Używanie opisowych nazw zmiennych
  • Dodawanie dokumentacji dla funkcji i klas
  • Komentowanie kodu w razie potrzeby
  • Dodawanie wskazówek typu dla argumentów i zwracanych typów funkcji

Specjalne przypadki nie są wystarczająco wyjątkowe, aby łamać zasady.

Należy – w miarę możliwości – stosować się do zasad języka i zalecanych najlepszych praktyk.

Ale czy zawsze jest to możliwe? Nie, dlatego mamy następny aforyzm.

Chociaż praktyczność wygrywa z czystością.

Jest to kontynuacja poprzedniego aforyzmu. Chociaż zaleca się przestrzeganie zasad języka, w niektórych przypadkach zupełnie dobrze jest nie przestrzegać niektórych zasad.

Błędy nigdy nie powinny przechodzić po cichu.

W Pythonie błędy wykonawcze są dość powszechne. Jako dobrą praktykę należy zawsze obsługiwać błędy i nie wyciszać ich w ramach szybkiej naprawy.

Możesz przewidzieć i wdrożyć odpowiednią obsługę błędów — dla różnych typów błędów:

try:  
    # doing this
except ErrorType1:
    # do something
except ErrorType2:
    # do something else
...

Powinieneś unikać nagich i ogólnych wyjątków. Nowsze wersje Pythona (od Pythona 3.11) obsługują łańcuchy wyjątków i grupy wyjątków, aby wykonywać bardziej wyrafinowaną obsługę wyjątków.

Chyba, że ​​zostanie wyraźnie uciszony.

Wynika to z poprzedniego aforyzmu. Jeśli projekt wymaga wyciszenia błędu lub pozwala na to, należy to zrobić jawnie.

Na przykład: podczas łączenia się z bazą danych możesz napotkać błąd operacyjny z powodu nieprawidłowych informacji konfiguracyjnych. Spróbuj połączyć się przy użyciu niestandardowej konfiguracji. W przypadku wystąpienia błędu operacyjnego użyj domyślnej konfiguracji i spróbuj połączyć się z bazą danych.

try:
   # connecting using custom config
except OperationalError:
   # connect using default config

W obliczu niejednoznaczności odrzuć pokusę zgadywania.

Ten aforyzm w Zen Pythona jest oczywisty. W razie wątpliwości nie zgaduj. Ale uruchom kod i sprawdź dane wyjściowe. Następnie w zależności od tego, czy masz pożądane zachowanie, popraw czytelność lub zmodyfikuj logikę w razie potrzeby.

Weźmy następujący prosty przykład z krotką Boolean:

>>> True, True == (True, True)
(True, False)
>>> True, (True == (True, True))
(True, False)
>>> (True, True) == (True, True)
True

Powinien być na to jeden – a najlepiej tylko jeden – oczywisty sposób.

Aby wykonać określone zadanie, powinien istnieć jeden i tylko jeden zalecany pythonowy sposób jego wykonania. Jednak dla każdego problemu możemy mieć wiele rozwiązań.

Nawet w prostym przykładzie odwrócenia łańcucha przyjrzeliśmy się rozwiązaniu rekurencyjnemu, dzieleniu łańcuchów i metodzie join().

Jest to również wewnętrzny żart, biorąc pod uwagę niespójne użycie em-myślników. Zwykle używamy kresek bez spacji wiodących i końcowych. Lub używamy go zarówno ze spacją wiodącą, jak i końcową.

Oto, co możemy wywnioskować. Aforyzm, który podkreśla, że ​​powinien istnieć jeden — i tylko jeden — pythonowy sposób robienia rzeczy, sam w sobie można zapisać na więcej niż dwa sposoby.

Chociaż ten sposób może na początku nie być oczywisty, chyba że jesteś Holendrem.

Napisane lekko, odnosi się to do Guido Van Rossuma, twórcy Pythona (który jest Holendrem). (Najbardziej) Pythoniczny sposób wykonania określonego zadania — przychodzi naturalnie tylko twórcom Pythona.

Tak więc programiści wymagają doświadczenia — i uczenia się na podstawie doświadczenia — aby lepiej wykorzystywać funkcje języka.

Teraz jest lepiej niż nigdy.

Podobnie jak kilka innych aforyzmów w Zen Pythona, ten również można interpretować na kilka różnych sposobów.

Jedna z interpretacji jest taka, że ​​jako programista dość często zwlekasz z rozpoczęciem kodowania projektu. Zamiast czekać z zaplanowaniem najdrobniejszych szczegółów projektu, lepszym pomysłem jest rozpoczęcie go teraz.

Inna możliwa interpretacja jest następująca: kod, który działa w skończonej liczbie kroków — i kończy działanie — jest często lepszy niż kod, który zawiera błędy i utknął w nieskończonej pętli.

Chociaż nigdy nie jest często lepsze niż teraz.

Ten aforyzm zdaje się przeczyć poprzedniemu. Chociaż lepiej nie zwlekać, warto przemyśleć problem i odpowiednio zaprojektować kod.

Kodowanie modułu — bez odpowiedniego przemyślenia — najeżonego zapachami kodu i antywzorcami to zły pomysł. Ponieważ taki kod jest trudny do refaktoryzacji i wdrożenia środków naprawczych.

Jeśli implementacja jest trudna do wyjaśnienia, jest to zły pomysł.

Każdą logikę — bez względu na to, jak złożona może być — zawsze można zaimplementować w formie łatwej do wyjaśnienia i zrozumienia.

Jeśli implementacja jest trudna do wyjaśnienia, prawdopodobnie jest to niepotrzebna złożoność. Kod można modyfikować lub refaktoryzować, aby łatwiej było go śledzić.

Jeśli implementacja jest łatwa do wyjaśnienia, może to być dobry pomysł.

Jest to związane z poprzednim aforyzmem i również jest oczywiste. Jeśli implementację można wyjaśnić w prosty sposób, prawdopodobnie jest to dobry pomysł.

Ponieważ taki kod, którego implementację można opisać — w prostych słowach — z dużym prawdopodobieństwem będzie czytelny i łatwy do naśladowania — przy minimalnej złożoności.

Przestrzenie nazw to świetny pomysł — róbmy ich więcej!

W Pythonie dostęp do obiektów w określonym zakresie można uzyskać za pomocą ich nazw w ich przestrzeni nazw. Na przykład możesz utworzyć klasę i użyć jej jako szablonu do tworzenia instancji klasy. Teraz wszystkie zmienne instancji będą znajdować się w przestrzeni nazw instancji.

Dzięki temu możemy używać obiektów o tej samej nazwie — bez konfliktów — ponieważ znajdują się one w różnych przestrzeniach nazw. Jednak należy ich używać tylko w razie potrzeby i upewnić się, że prostota i czytelność kodu nie zostaną naruszone.

Wniosek

To wszystko w tym samouczku! Mam nadzieję, że ten przewodnik pomógł ci zrozumieć, w jaki sposób Zen Pythona kładzie nacisk na styl kodu i dobre praktyki kodowania w Pythonie. Im więcej kodujesz, tym lepiej to robisz.

Jeśli chcesz dowiedzieć się, jak pisać zwięzły i czytelny kod, przeczytaj ten artykuł na temat jednowierszowych tekstów w języku Python.