Python Blog

Różne ciekawostki ze świata Pythona i nie tylko.


Pisanie kodu Pythona z pomocą PEP8, część 2.

Zalecane zasady pisania pięknego kodu w Pythonie opisane w PEP8, cd.

Komentarze w kodzie

Powinieneś używać komentarzy do dokumentowania kodu w trakcie jego pisania. Ważne jest, aby dokumentować swój kod tak, abyś ty i wszyscy współpracownicy mogli go zrozumieć. Kiedy ty lub ktoś inny czyta komentarz, powinien być w stanie łatwo zrozumieć kod, do którego komentarz się odnosi i wiedzieć, jak pasuje on do reszty kodu.

Oto kilka kluczowych punktów, o których należy pamiętać podczas dodawania komentarzy do kodu:

  • Ogranicz długość linii komentarzy i docstringów do 72 znaków.
  • Używaj pełnych zdań, zaczynając od wielkiej litery.
  • Pamiętaj, aby aktualizować komentarze w przypadku zmiany kodu.

Mając na uwadze te kluczowe punkty, nadszedł czas, aby przyjrzeć się szczegółom zaleceń PEP8 dotyczących komentarzy.

Komentarze blokowe

Komentarze blokowe służą do dokumentowania niewielkiego fragmentu kodu. Są one przydatne, gdy trzeba napisać kilka wierszy kodu, aby wykonać pojedynczą akcję, taką jak import danych z pliku lub aktualizacja wpisu w bazie danych. Są one ważne, aby pomóc innym zrozumieć cel i funkcjonalność danego bloku kodu.

PEP8 zapewnia następujące zasady pisania komentarzy blokowych:

  • Komentarze blokowe powinny być wcięte do tego samego poziomu co kod, który opisują.
  • Rozpocznij każdą linię znakiem #, po którym następuje pojedyncza spacja.
  • Oddzielaj akapity linią zawierającą pojedynczy znak #.

Komentarze blokowe służą do dokumentowania niewielkiego fragmentu kodu. Są one przydatne, gdy trzeba napisać kilka wierszy kodu, aby wykonać pojedynczą akcję, taką jak import danych z pliku lub aktualizacja wpisu w bazie danych. Są one ważne, aby pomóc innym zrozumieć cel i funkcjonalność danego bloku kodu.

PEP8 zapewnia następujące zasady pisania komentarzy blokowych:

  • Komentarze blokowe powinny być wcięte do tego samego poziomu co kod, który opisują.
  • Rozpocznij każdą linię znakiem #, po którym następuje pojedyncza spacja.
  • Oddzielaj akapity linią zawierającą pojedynczy znak #.

Oto komentarz blokowy wyjaśniający funkcję pętli for. Zwróć uwagę, że zdanie jest zawijane do nowej linii, aby zachować 79-znakowy limit linii:

for number in range(0, 10):
    # Loop over `number` ten times and print out the value of `number`
    # followed by a newline character.
    print(i, "\n")

Czasami, jeśli kod jest bardzo techniczny, konieczne jest użycie więcej niż jednego akapitu w komentarzu blokowym:

# Calculate the solution to a quadratic equation using the quadratic
# formula.
#
# A quadratic equation has the following form:
# ax**2 + bx + c = 0
#
# There are always two solutions to a quadratic equation, x_1 and x_2.
x_1 = (-b + (b**2 - 4 * a * c) ** (1 / 2)) / (2 * a)
x_2 = (-b - (b**2 - 4 * a * c) ** (1 / 2)) / (2 * a)

Jeśli kiedykolwiek masz wątpliwości, który typ komentarza jest odpowiedni, często najlepszym rozwiązaniem są komentarze blokowe. Używaj ich tak często, jak to możliwe w całym kodzie, ale pamiętaj, aby je aktualizować, jeśli wprowadzisz zmiany w kodzie!

Komentarze w linii

Komentarze w linii (ang. inline comments) wyjaśniają pojedynczą instrukcję w fragmencie kodu. Są przydatne do przypomnienia lub wyjaśnienia innym, dlaczego dana linia kodu jest konieczna. Oto, co PEP 8 ma do powiedzenia na ich temat:

  • Używaj komentarzy inline oszczędnie.
  • Pisz komentarze inline w tym samym wierszu, co instrukcja, do której się odnoszą.
  • Oddzielaj komentarze inline od instrukcji dwoma lub więcej spacjami.
  • Zaczynaj komentarze inline od znaku # i pojedynczej spacji, tak jak w przypadku komentarzy blokowych.
  • Nie używaj ich do wyjaśniania rzeczy oczywistych.

Poniżej znajduje się przykład komentarza inline:

x = 5  # To komentarz w linii

Czasami komentarze inline mogą wydawać się niezbędne, ale zamiast tego można użyć lepszych konwencji nazewnictwa. Oto przykład:

x = "John Smith"  # Student Name

W tym przypadku komentarz inline dostarcza dodatkowych informacji. Jednak używanie x jako nazwy zmiennej dla imienia osoby jest złą praktyką. Nie ma potrzeby stosowania komentarza inline, jeśli zmienisz nazwę zmiennej:

student_name = "John Smith"

Wreszcie, komentarze inline, takie jak te pokazane poniżej, są złą praktyką, ponieważ stwierdzają oczywistość i zaśmiecają kod:

empty_list = []  # Initialize empty list

x = 5
x = x * 5  # Multiply x by 5

Komentarze wbudowane są bardziej szczegółowe niż komentarze blokowe i łatwo jest je dodać, gdy nie są konieczne, co prowadzi do bałaganu. Możesz uciec od używania tylko komentarzy blokowych. O ile nie masz pewności, że potrzebujesz komentarza inline, twój kod będzie bardziej zgodny z przewodnikiem po stylu dla kodu Pythona, jeśli będziesz używać tylko komentarzy blokowych.

Komentarze do dokumentacji

Komentarze do dokumentacji (docstrings) to ciągi ujęte w potrójne podwójne cudzysłowy (""") lub potrójne pojedyncze cudzysłowy ('''), które pojawiają się w pierwszym wierszu dowolnej funkcji, klasy, metody lub modułu.

"""To jest docstring."""

# ...

Docstringów używa się do wyjaśnienia i udokumentowania określonego bloku kodu. Są one ważną częścią Pythona, a dostęp do docstring obiektu można uzyskać za pomocą jego atrybutu .doc lub funkcji help():

>>> import documented_module

>>> documented_module.__doc__
'This is a docstring.'

>>> help(documented_module)
Help on module documented_module:

NAME
    documented_module - This is a docstring.

FILE
    ./documented_module.py

Podczas gdy PEP 8 wspomina o docstringach, stanowią one na tyle obszerny temat, że istnieje osobny dokument, PEP 257, który jest w całości poświęcony docstringom.

Głównym wnioskiem jest to, że docstringi są ustrukturyzowanym podejściem do dokumentowania kodu Pythona. Powinieneś pisać je dla wszystkich publicznych modułów, funkcji, klas i metod.

Jeśli implementacja jest prosta, można użyć jednowierszowego docstringu, w którym cały docstring znajduje się w tej samej linii:

def adder(a, b):
    """Add a to b."""
    return a + b

Jeśli implementacja jest bardziej złożona, będziesz potrzebował więcej linii, aby stworzyć użyteczny docstring. W takim przypadku należy rozpocząć od ogólnego opisu w pierwszym wierszu i zakończyć go kropką.

Następnie można użyć więcej tekstu do udokumentowania obiektu kodu. W tej części docstring można również zawrzeć opis argumentów i wartości zwracanej.

Na koniec należy umieścić trzy cudzysłowy, które kończą wielowierszowy docstring w jednej linii:

def quadratic(a, b, c):
    """Solve quadratic equation via the quadratic formula.

    A quadratic equation has the following form:
    ax**2 + bx + c = 0

    There always two solutions to a quadratic equation: x_1 & x_2.
    """
    x_1 = (-b + (b**2 - 4 * a * c) ** (1 / 2)) / (2 * a)
    x_2 = (-b - (b**2 - 4 * a * c) ** (1 / 2)) / (2 * a)

    return x_1, x_2

Bardziej szczegółowy samouczek dotyczący dokumentowania kodu Pythona, który obejmuje również różne style docstringów, znajduje się w Dokumentowanie kodu Pythona: Kompletny przewodnik..

Białe znaki w wyrażeniach

Białe znaki mogą być bardzo pomocne w wyrażeniach i instrukcjach - jeśli są prawidłowo używane. Jeśli nie ma wystarczającej ilości białych znaków, kod może być trudny do odczytania, ponieważ wszystko jest ze sobą powiązane. Z kolei zbyt duża ilość białych znaków może utrudniać wizualne łączenie powiązanych ze sobą terminów w instrukcji.

Białe spacje wokół operatorów binarnych

Aby zapewnić najlepszą czytelność zgodnie z PEP 8, następujące operatory binarne należy otoczyć pojedynczą spacją po obu stronach:

  • Operatory przypisania: =, +=, -= itd.

  • Porównania: ==, !=, >, <. >=, <=, is, in i not in

  • Wyrażenia logiczne: and, not, oraz or

Gdy używasz znaku równości (=), aby przypisać wartość domyślną do argumentu, nie otaczaj go spacjami:

# ✅ Zalecane
def function(default_parameter=5):
    # ...


# ❌ Nie zalecane
def function(default_parameter = 5):
    # ...

Unikanie białych znaków w celu wskazania domyślnych wartości argumentów sprawia, że definicje funkcji i metod są bardziej zwięzłe.

Jeśli w instrukcji występuje więcej niż jeden operator, dodanie pojedynczej spacji przed i po każdym operatorze może wyglądać myląco. Zamiast tego lepiej jest dodawać białe znaki tylko wokół operatorów o najniższym priorytecie, zwłaszcza podczas wykonywania manipulacji matematycznych. Oto kilka przykładów:

# ✅ Zalecane
y = x**2 + 5
z = (x+y) * (x-y)

# ❌ Nie zalecane
y = x ** 2 + 5
z = (x + y) * (x - y)

Jeśli użyjesz białych znaków do pogrupowania wielu operatorów zgodnie z ich pierwszeństwem, sprawi to, że Twój kod będzie bardziej czytelny.

Można to również zastosować do instrukcji if, w których występuje wiele warunków:

if x > 5 and x % 2 == 0:
    print("x is larger than 5 and divisible by 2!")

W powyższym przykładzie operator and ma najniższy priorytet. Dlatego też bardziej zrozumiałe może być wyrażenie instrukcji if w poniższy sposób:

if x>5 and x%2==0:
    print("x is larger than 5 and divisible by 2!")

W wycinkach dwukropki działają jak operatory binarne. W związku z tym obowiązują zasady opisane w poprzedniej sekcji, a po obu stronach powinna znajdować się taka sama ilość białych znaków. Poniższe przykłady wycinków list są poprawne:

a_list[3:4]

# Treat the colon as the operator with lowest priority.
a_list[x+1 : x+2]

# In an extended slice, you must surround both colons
# with the same amount of whitespace.
a_list[3:4:5]
a_list[x+1 : x+2 : x+3]

# You omit the space if you omit a slice parameter.
a_list[x+1 : x+2 :]

Podsumowując, większość operatorów należy otaczać białymi znakami. Istnieją jednak pewne wyjątki od tej reguły, takie jak argumenty funkcji lub łączenie wielu operatorów w jednej instrukcji.

PEP 8 określa również kilka innych przypadków, w których należy unikać białych znaków:

  • Bezpośrednio wewnątrz nawiasów, nawiasów klamrowych lub nawiasów kwadratowych:
    # ✅ Zalecane
    numbers = [1, 2, 3]
    
    # ❌ Nie zalecane
    numbers = [ 1, 2, 3, ] 
    
  • Przed przecinkiem, średnikiem lub dwukropkiem:
    x = 5
    y = 6
    
    # ✅ Zalecane
    print(x, y)
    
    # ❌ Nie zalecane
    print(x , y)
    
  • Przed nawiasem otwierającym, który rozpoczyna listę argumentów wywołania funkcji:
    def double(x):
    return x * 2
    
    # ✅ Zalecane
    double(3)
    
    # ❌ Nie zalecane
    double (3)
    
  • Przed otwartym nawiasem rozpoczynającym indeks lub wycinek:
    # ✅ Zalecane
    a_list[3]
    
    # ❌ Nie zalecane
    a_list [3] 
    
  • Pomiędzy przecinkiem końcowym a nawiasem zamykającym:
    # ✅ Zalecane
    a_tuple = (1,)
    
    # ❌ Nie zalecane
    a_tuple = (1, )
    
  • Aby wyrównać operatory przypisania:
    # ✅ Zalecane
    var1 = 5
    var2 = 6
    some_long_var = 7
    
    # ❌ Nie zalecane
    var1          = 5
    var2          = 6
    some_long_var = 7
    

Najważniejszym wnioskiem jest upewnienie się, że nigdzie w kodzie nie ma końcowych białych znaków. Istnieją również inne przypadki, w których PEP8 odradza dodawanie dodatkowych białych znaków, takich jak bezpośrednio wewnątrz nawiasów, a także przed przecinkami i dwukropkami. Nie należy również dodawać dodatkowych białych znaków w celu wyrównania operatorów.

Zalecenia dotyczące programowania

Często okazuje się, że w Pythonie, podobnie jak w każdym innym języku programowania, istnieje kilka sposobów na wykonanie podobnej czynności. W tej sekcji zobaczysz kilka sugestii, które PEP8 zapewnia w celu usunięcia tej niejednoznaczności i zachowania spójności.

Nie porównuj wartości logicznych do True lub False za pomocą operatora równoważności. Często będziesz musiał sprawdzić, czy wartość logiczna jest prawdziwa czy fałszywa. Można to zrobić za pomocą instrukcji takiej jak ta poniżej:

    # Nie zalecane
    is_bigger = 6 > 5
    if is_bigger == True:
        return "6 is bigger than 5"

Użycie operatora równoważności (==) jest tutaj niepotrzebne. bool może przyjmować tylko wartości True lub False. Wystarczy napisać następującą instrukcję:

    # Zalecane
    is_bigger = 6 > 5
    if is_bigger:
        return "6 is bigger than 5"

Ten sposób wykonywania instrukcji if z wartością logiczną wymaga mniej kodu i jest prostszy, więc PEP8 zachęca do jego stosowania.

Wykorzystaj fakt, że puste sekwencje są fałszywe w instrukcjach if. Jeśli chcesz sprawdzić, czy lista jest pusta, możesz pokusić się o sprawdzenie jej długości. Jeśli lista jest pusta, to jej długość wynosi 0, co jest równoważne wartości False, gdy używasz jej w instrukcji if. Oto przykład:

    a_list = []
    if not len(a_list):
        print("List is empty!")

Jednak w Pythonie każda pusta lista, łańcuch lub krotka jest False. Można zatem wymyślić prostszą alternatywę dla powyższego rozwiązania:

    a_list = []
    if not a_list:
        print("List is empty!")

Podczas gdy oba przykłady spowodują wypisanie List is empty!, druga opcja jest prostsza do odczytania i zrozumienia, więc PEP8 zachęca do jej stosowania.

Używaj is not zamiast not … is w instrukcjach if. Jeśli próbujesz sprawdzić, czy zmienna ma zdefiniowaną wartość, istnieją dwie opcje. Pierwszą z nich jest ocena instrukcji if z x is not None, jak w poniższym przykładzie:

    if x is not None:
        return "x exists!"

Drugą opcją byłoby sprawdzenie, czy x to None, a następnie wykonanie instrukcji if opartej na braku wyniku:

    if not x is None:
        return "x exists!"

Podczas gdy Python poprawnie oceni obie opcje, pierwsza z nich jest prostsza, więc PEP 8 zachęca do jej stosowania.

Nie używaj if x:, gdy masz na myśli if x is not None:. Czasami możesz mieć funkcję z argumentami, które domyślnie są None. Częstym błędem podczas sprawdzania, czy taki argument, arg, ma inną wartość, jest użycie następującego wyrażenia:

    if arg:
        print(arg)

Ten kod sprawdza, czy arg jest prawdą. Zamiast tego chcesz sprawdzić, czy arg nie jest None, więc lepiej jest użyć następującego kodu:

    if arg is not None:
        print(arg)

Błąd polega na założeniu, że not None i prawda są równoważne, ale tak nie jest. Mogłeś ustawić arg na pustą listę ([]). Jak zauważyłeś powyżej, puste listy są również oceniane jako False w Pythonie. Tak więc, nawet jeśli przypisałeś wartość do arg, warunek nie jest spełniony, a Python nie wykona kodu w treści instrukcji if:

>>> arg = []

>>> if arg:
...     print(arg)
...

>>> if arg is not None:
...     print(arg)
...
[]

Można zauważyć, że oba podejścia dają różne wyniki podczas pracy z wartościami, które są fałszywe w Pythonie.

Użyj .startswith() i .endswith() zamiast wycinania. Jeśli próbujesz sprawdzić, czy ciąg word był poprzedzony lub zakończony słowem cat, rozsądne może wydawać się użycie wycinka listy. Jednak wycinanie listy jest podatne na błędy i trzeba zakodować liczbę znaków w przedrostku lub przyrostku. Nie jest też jasne dla kogoś mniej zaznajomionego z wycinaniem list w Pythonie, co próbujesz osiągnąć:

    if word[:3] == "cat":
        print("The word starts with 'cat'")

Nie jest to jednak tak czytelne jak użycie .startswith():

    if word.startswith("cat"):
        print("The word starts with 'cat'")

Podobnie, ta sama zasada ma zastosowanie podczas sprawdzania przyrostków. Poniższy przykład pokazuje, jak można sprawdzić, czy ciąg kończy się na “jpg”:

    if file_name[-4:] == ".jpg":
        print("The file is a JPEG")

Chociaż wynik jest poprawny, notacja jest nieco niezgrabna i trudna do odczytania. Zamiast tego można użyć .endswith(), jak w poniższym przykładzie:

    if file_name.endswith(".jpg"):
        print("The file is a JPEG")

Podobnie jak w przypadku większości tych zaleceń programistycznych, celem jest czytelność i prostota. W Pythonie istnieje wiele różnych sposobów na wykonanie tej samej akcji, więc wskazówki dotyczące wyboru metod mogą być pomocne.

Przy opracowywaniu materiału korzystałem z zasobów Real Python.