course

Czym jest Zen Pythona?
Zen Pythona to zbiór dziewiętnastu aforyzmów, które stanowią zasady przewodnie projektu Pythona. Pierwotnie miało być ich dwadzieścia; jednak Guido van Rossum, twórca Pythona, wciąż nie dodał ostatniego aforyzmu, jak planował Tim Peters, autor Zen Pythona. Guido miał stwierdzić, że brakujący dwudziesty aforyzm to „jakiś dziwny wewnętrzny żart Tima Petersa”.
Mimo braku dwudziestego aforyzmu, Zen Pythona został w 2004 roku ustandaryzowany jako PEP 20 z uwagi na ogromny wpływ na proces tworzenia oprogramowania przez programistów Pythona. Stosowanie się do Zen Pythona nie jest obowiązkowe, ale warto go znać i mieć w pamięci. Jeśli kiedykolwiek zapomnisz lekcje, możesz łatwo odświeżyć pamięć, uruchamiając w interpreterze Pythona import this.
>>> 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!
W tym artykule omówimy każdą lekcję bardziej szczegółowo.
#1 Piękne jest lepsze niż brzydkie
Jeśli programujesz, bez wątpienia masz wysokie umiejętności. Rozwiązywanie problemów za pomocą kodu nie jest proste, ale jako Pythonista oczekuje się od ciebie więcej. Piękny kod jest lepszy niż brzydki. Błyskawiczna kariera Pythona wynika częściowo z jego przystępności: jest prosty, czytelny i elegancki. Jako deweloper Pythona masz odpowiedzialność, by utrzymywać „pythoniczny” standard, pisząc piękny kod.
Czym jest piękny kod? Cóż, to kwestia subiektywna — piękno tkwi w oku patrzącego i tak dalej. Dobrą, prostą zasadą, która trzyma w ryzach, jest pisanie czystego, czytelnego kodu, który inni deweloperzy łatwo zrozumieją. Na przykład:
"""
Initial code was created by Vishal Sharma
See: https://towardsdatascience.com/the-zen-of-python-a-guide-to-pythons-design-principles-93f3f76d088a
"""
def collatz(num):
if num % 2 == 0:
return num // 2
else:
return 3 * num + 1
number = input("Enter a number: ")
while number != 1:
number = collatz(int(number))
print(number)
"""
Enter a number: 5
16
8
4
2
1
"""
Powyższy kod działa bez zarzutu, ale niekoniecznie jest piękny. W funkcji collatz mamy zbędny blok else, który można usunąć w ten sposób:
def collatz(num):
if num % 2 == 0:
return num // 2
return 3 * num + 1
Dlatego, jeśli masz dwa działające fragmenty kodu, wybieraj ten prostszy, bardziej czytelny i łatwiejszy do zrozumienia.
#2 Jawne jest lepsze niż domyślne
Nazwanie jakiegokolwiek aforyzmu „samowyjaśniającym się” to duży błąd — choć i tak ludzie tak robią. Sednem „jawne jest lepsze niż domyślne” jest to, że rozwlekły kod jest lepszy od pośredniego. Rób wszystko, by funkcjonalność twojego kodu nie kryła się za niejasnym językiem. Osoba bez wcześniejszej wiedzy o twoim programie powinna zrozumieć, co dzieje się w kodzie.
#3 Proste jest lepsze niż złożone
Prawdopodobnie parskniesz śmiechem, gdy kumpel wyciągnie myjkę ciśnieniową, żeby umyć talerze po kolacji. To nie tylko bardzo głupi pomysł — talerze mogą się uszkodzić. Tak samo stosowanie bardzo złożonego rozwiązania do prostego problemu programistycznego jest skrajnie niepraktyczne. Nie ma potrzeby nadmiernie komplikować rozwiązań, nawet jeśli wydaje ci się, że to doda ci splendoru. Możesz narobić więcej szkód niż pożytku — a w Pythonie tego się nie docenia.
Spójrz na tę funkcję odwracającą łańcuch znaków:
def reverse_string(string):
if len(string) == 1:
return string
else:
return reverse_string(string[1:]) + string[0]
string = "kurtis"
print(reverse_string(string))
"""
sitruk
"""
Powyższy kod rozwiązuje problem, ale jest niepotrzebnie złożony. Mając dobrą znajomość Pythona, wiesz, że łańcuch można odwrócić przez indeksowanie.
string = "kurtis"
# annonymous function solution
reverse_string = lambda x: x[::-1]
# function solution
def reverse(string):
return string[::-1]
print(reverse_string(string))
print(reverse(string))
"""
sitruk
sitruk
"""
#4 Złożone jest lepsze niż skomplikowane
Mycie talerzy myjką ciśnieniową jest niepraktyczne: lepiej sprawdzi się proste rozwiązanie, jak bieżąca woda. Ale co, jeśli trzeba wyczyścić 4x4 Range Rovera? Poprzedni aforyzm i ten przypominają, że do jednego problemu można zastosować zarówno proste, jak i złożone techniki. Czyszczenie 4x4 Range Rovera nie jest prostym zadaniem i próba użycia tych samych metod co przy zmywaniu naczyń może zadziałać, ale będzie bardziej skomplikowana niż użycie myjki. Dlatego preferuj prostotę nad złożonością, ale gdy prostota jest niepraktyczna, lepiej wybrać złożoność — znaj granice prostoty.
#5 Płaskie jest lepsze niż zagnieżdżone
Programiści lubią porządkować rzeczy w kategorie, podkategorie i pod-podkategorie, by rozdzielać funkcjonalności. Choć brzmi to schludnie, taki porządek może wprowadzać więcej zamieszania niż ładu.
Nie ma nic złego w umieszczeniu całego kodu w jednym, najwyższym module: dzięki temu nie będziesz musieć pisać from spam.foo.bar.john.doe import chicken , by dostać się do konkretnej funkcji. Im więcej podkategorii dodasz, tym bardziej skomplikowany staje się kod. Rób wszystko, by trzymać się płaskiej struktury, kiedy to możliwe.
#6 Rzadkie jest lepsze niż gęste
Programistów uznaje się za jednych z najbardziej inteligentnych ludzi, więc nie musisz podkreślać swojego intelektu przesadnymi trikami. Najczęściej spotkasz „jak zrobić [jakiś złożony task] w 1 linii kodu”. Czasem one-linery mają sens, ale unikaj sytuacji, w których poświęcasz czytelność tylko po to, by upchnąć wszystko w jednej linii.
Przykładowy fragment:
"""
Code source Al Sweigart
See: https://inventwithpython.com/blog/author/al-sweigart.html0
"""
print('\n'.join("%i bytes = %i bits which has %i possible values." % (j, j*8, 256**j-1) for j in (1 << i for i in range(8))))
"""
1 bytes = 8 bits which has 255 possible values.
2 bytes = 16 bits which has 65535 possible values.
4 bytes = 32 bits which has 4294967295 possible values.
8 bytes = 64 bits which has 18446744073709551615 possible values.
16 bytes = 128 bits which has 340282366920938463463374607431768211455 possible values.
32 bytes = 256 bits which has 115792089237316195423570985008687907853269984665640564039457584007913129639935 possible values.
64 bytes = 512 bits which has 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095 possible values.
128 bytes = 1024 bits which has 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215 possible values.
"""
Kod działa, ale jest koszmarnie trudny do zrozumienia: ciasny i nieczytelny.
Zobaczmy, jak ten sam kod wygląda w luźniejszej formie:
# Sparse example
bytes_and_bits = {j: j*8 for j in (1 << i for i in range(8))}
for bytes, bits in bytes_and_bits.items():
print(f"{bytes} bytes = {bits} which has {256**bytes-1} possible values")
"""
1 bytes = 8 which has 255 possible values
2 bytes = 16 which has 65535 possible values
4 bytes = 32 which has 4294967295 possible values
8 bytes = 64 which has 18446744073709551615 possible values
16 bytes = 128 which has 340282366920938463463374607431768211455 possible values
32 bytes = 256 which has 115792089237316195423570985008687907853269984665640564039457584007913129639935 possible values
64 bytes = 512 which has 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084095 possible values
128 bytes = 1024 which has 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137215 possible values
"""
Ten kod jest znacznie czytelniejszy i robi dokładnie to samo co one-liner.
#7 Czytelność ma znaczenie
Jeśli jeszcze się nie domyśliłeś, w Pythonie czytelność ma znaczenie. Kod piszesz raz. Ale najpewniej będzie czytany wielokrotnie. Mając to na uwadze, skracanie nazw zmiennych i funkcji przez wyrzucanie samogłosek to kiepski pomysł: jeśli możesz nazwać funkcję create_lst albo create_list, wybierz tę drugą.
#8 Przypadki szczególne nie są na tyle szczególne, by łamać zasady
Python [i programowanie ogólnie] pełen jest dobrych praktyk, których warto się trzymać. Lepiej je stosować niż iść własną drogą, co często kończy się niespójnym, nieczytelnym kodem.
#9 Chociaż praktyczność przeważa nad czystością
Lekcja dziewiąta rozwija ósmą. Tak, lepiej trzymać się dobrych praktyk, ale kurczowe przestrzeganie zasad również może prowadzić do nieczytelnego kodu. Dlatego każda reguła może mieć wyjątek. Jeśli twój sposób rozwiązania problemu jest bardziej praktyczny, czytelniejszy i łatwiejszy do zrozumienia, lepiej odejść od utartych dobrych praktyk.
#10 Błędy nigdy nie powinny przechodzić po cichu
Cichy błąd to sytuacja, gdy program zwraca kod błędu lub None zamiast zgłosić wyjątek. Lepiej, by program się wykrzaczył, niż by błąd został wyciszony i aplikacja działała dalej. Na dłuższą metę wyciszanie błędów prowadzi do trudnych do wykrycia bugów.
#11 Chyba że jawnie wyciszone
Lekcja jedenasta rozwija dziesiątą. Zdarzają się sytuacje, gdy chcesz zignorować błędy w programie. W takich przypadkach najlepszą praktyką jest jawne wyciszenie błędu w kodzie.
#12 W obliczu niejednoznaczności oprzyj się pokusie zgadywania
Komputery robią tylko to, co im każemy: jeśli twój kod nie zachowuje się tak, jak chcesz, to dlatego, że robi to, co mu kazałeś. Próba naprawy zachowania przez ślepe testowanie kolejnych rozwiązań, aż któreś zadziała, to kiepska strategia — możesz zamaskować problem zamiast go rozwiązać. Oprzyj się pokusie. Zamiast tego przemyśl logikę problemu i zastosuj krytyczne myślenie, by znaleźć właściwe rozwiązanie.
#13 Powinien istnieć jeden — i najlepiej tylko jeden — oczywisty sposób, by to zrobić
Motto języka Perl brzmi: „Da się to zrobić na więcej niż jeden sposób!”. Nadmiar opcji często prowadzi do paraliżu decyzyjnego. Podobnie jest, gdy istnieje wiele sposobów zapisania kodu osiągającego ten sam cel. Masz większą elastyczność w pisaniu, ale teraz musisz poznać wszystkie możliwe warianty, by móc czytać cudzy kod — ten dodatkowy wysiłek jest zbędny.
#14 Choć ten sposób może nie być oczywisty od razu, chyba że jesteś Holendrem
Ten aforyzm pokazuje poczucie humoru Tima Petersa: Guido van Rossum, twórca języka Python i Benevolent Dictator For Life (BDFL) Pythona, jest Holendrem. To żart przypominający, że rozumienie i pamiętanie reguł języka Python jest trudne dla wszystkich poza jego twórcą.
#15 Teraz jest lepsze niż nigdy
Ten aforyzm mówi, że kod utykający w nieskończonej pętli lub zawieszający się jest gorszy niż kod, który tego nie robi.
#16 Chociaż nigdy bywa lepsze niż *natychmiast* teraz
Kontynuując poprzednią lekcję: lepiej poczekać, aż program zakończy działanie, niż przerwać go przedwcześnie i otrzymać błędne wyniki.
#17 Jeśli implementację trudno wyjaśnić, to zły pomysł
Nie wystarczy, że sam rozumiesz własne implementacje — „wiem, o co mi chodziło” nie wystarcza. Programowanie to praca zespołowa i jeśli nie potrafisz wyjaśnić implementacji kolegom, bardzo możliwe, że nadmiernie skomplikowałeś rozwiązanie.
#18 Jeśli implementację łatwo wyjaśnić, to może być dobry pomysł
Jednak łatwość wyjaśnienia nie oznacza automatycznie, że kod jest dobry — oznacza tylko, że łatwo go wytłumaczyć. Kod może wciąż być kiepski, ale fakt, że łatwo go wyjaśnić, sugeruje, że idziesz w dobrą stronę.
#19 Przestrzenie nazw to kapitalny pomysł — róbmy ich więcej!
Przestrzeń nazw to w Pythonie abstrakcja do organizowania nazw przypisanych obiektom w programie. Mając daną przestrzeń nazw i zasięg, Python potrafi ustalić, do jakiego obiektu odwołujesz się, wywołując nazwę symboliczną. Ten aforyzm mówi, że sposób, w jaki Python pod spodem organizuje nazwy symboliczne, jest naprawdę świetny.
Podsumowanie
W tym artykule przedstawiliśmy naszą interpretację Zen Pythona — wytycznych, które mają motywować programistów Pythona do pisania czystego, czytelnego kodu. Dla jednych Zen Pythona to ostateczny plan na bezproblemowe pisanie kodu, dla innych — rzecz mniej poważna. Zachęcamy, byś spróbował zastosować te zasady we własnym kodzie i na własnej skórze sprawdził, jak poprawiają pracę. Jeśli chcesz dowiedzieć się więcej o pisaniu zoptymalizowanego kodu, zajrzyj do kursu DataCamp Writing Efficient Python Code.