Како вам Зен оф Питхон може помоћи да напишете бољи код

Želite li unaprediti svoje veštine pisanja Python koda?

Evo kako vam „Zen Pythona“ može biti od pomoći u vašim prvim koracima ka tom cilju.

Python je izuzetno jednostavan za učenje. Ipak, pisanje idiomatskog i „Pythonic“ koda koji je lako održavati može predstavljati izazov, posebno za programere početnike. PEP-20 je predstavio „Zen Pythona“, pesmu Tima Petersa, koja ističe značaj pisanja Python koda koji se pridržava najboljih praksi.

Da biste pročitali „Zen Pythona“, možete pokrenuti Python REPL i uneti sledeće:

>>> 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!

Kao što vidite, većina aforizama u „Zen Pythona“ su razumljivi sami po sebi. Neke aforizme treba interpretirati u kombinaciji sa sledećim, dok su neki u suprotnosti sa ranijim izrekama. Bez obzira na to, „Zen Pythona“ je zabavan, zanimljiv i praktičan za čitanje!

Tumačenje „Zen Pythona“

Smatra se da „Zen Pythona“ sadrži 20 vodećih principa za programiranje u Pythonu. Međutim, do sada postoji samo 19 aforizama. Hajde da ih razmotrimo.

Lepše je nego ružno.

Ovaj aforizam naglašava važnost pisanja elegantnog i „Pythonic“ koda.

Sledeći isječak koda ima „miris koda“:

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

Funkcija:

  • Inicijalizuje praznu listu
  • Ima petlju unutar funkcije koja dodaje elemente na kraj liste i
  • Konačno vraća listu

Iako je ovo funkcionalno ispravno, nije „Pythonic“ i teško ga je održavati.

Možete ga napisati mnogo elegantnije koristeći generatore. Evo funkcije generatora ekvivalentne gore navedenoj:

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

Ili još bolje, možete koristiti sledeći izraz za razumevanje generatora:

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

Eksplicitno je bolje nego implicitno.

Kada pišete kod, nemojte ostavljati druge programere i korisnike da nagađaju o podrazumevanom ili implicitnom ponašanju koda. Budite eksplicitni. Uzmimo za primer uvoz sa džoker znakom:

from some_module import * # wildcard import
from some_other_module import *

result = some_function() # odakle je ovo došlo?

Izbegavajte uvoz sa džoker znakovima koliko god je moguće, jer nije eksplicitan i neefikasan. Budite precizni kada uvozite funkcije i klase iz drugih modula:

from some_module import this_function # eksplicitan uvoz

result = this_function() # sada znamo.

Jednostavno je bolje od složenog.

Ovaj aforizam kaže da kod treba da bude jednostavan i da treba izbegavati nepotrebnu složenost. Na primer, možda ćete želeti da obrnete string i primenite sledeće rekurzivno rešenje:

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

Iako ovo funkcioniše, ovo je verovatno preterano komplikovano rešenje za ovaj problem, s obzirom na to da postoje jednostavniji i više „Pythonic“ načini da se to uradi.

Evo pristupa sečenjem stringova:

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

A evo pristupa koji koristi ugrađene metode i funkcije:

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

Složeno je bolje od komplikovanog.

Šta onda ovaj sledeći aforizam u „Zen Pythona“ prenosi?

Obrtanje stringova u Pythonu je vrlo jednostavna operacija. U praksi, međutim, možda će nam trebati složenija logika. Evo prilično jednostavnog primera:

Recimo da treba da se povežete sa bazom podataka:

  • Prvo biste trebali analizirati TOML konfiguracionu datoteku kako biste dobili informacije o konfiguraciji baze podataka.
  • Konektor baze podataka treba da bude instaliran.
  • Zatim možete definisati funkciju za povezivanje sa bazom podataka, predviđanje grešaka pri povezivanju, implementaciju rukovanja greškama i još mnogo toga.
  • Konačno, nakon povezivanja sa bazom podataka, možete postaviti upit.

Iako je ovo i dalje dovoljno jednostavno, zahteva složeniju logiku u poređenju sa obrtanjem stringa. Ali to ne znači da mora biti komplikovano. I dalje možete efikasno koristiti funkcionalnost koda ugrađenih modula i organizovati svoj kod tako da ga drugi programeri mogu čitati, razumeti i doprinositi.

Ravna struktura je bolja od ugnežđene.

Ravnu strukturu je lakše analizirati i razumeti nego ugnežđenu strukturu. Dok radite na projektu, možda ćete biti u iskušenju da izolujete funkcionalnost kreiranjem zasebnih modula. Međutim, prevelika granulacija može biti preterana.

Ipak, možda ćete često morati da pređete izvan ravne strukture. Ali čak i ako vam je potrebno ugnežđivanje, svedite ga na minimum.

Evo primera:

from db_info.config.actions.parse.parse_config import parse_toml # preteško za analizu!
...

from db_config.parse_config import parse_toml # mnogo bolje!
...

Retko je bolje nego gusto.

Ako tek počinjete na svom programerskom putu, možda ćete biti u iskušenju da prekomerno koristite neke od karakteristika jezika. Razumevanje lista, na primer, je „Pythonic“, ali samo kada ih koristite tamo gde su potrebna.

Pogledajte sledeće razumevanje:

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)]

Razumevanje liste je previše gusto i teško za analizu. U ovom slučaju, korišćenje ekvivalentne for petlje sa uslovima bi bilo čitljivije. To znači da je razumevanje teško razumeti. 🙂

Čitljivost je bitna.

Uvek treba da pišete čitljiv kod. Evo nekoliko jednostavnih načina da poboljšate čitljivost koda:

  • Korišćenje opisnih imena promenljivih
  • Dodavanje dokumentacionih stringova za funkcije i klase
  • Komentarisanje koda gde je potrebno
  • Dodavanje napomena o tipovima za argumente i povratne tipove funkcija

Posebni slučajevi nisu dovoljno posebni da bi prekršili pravila.

Trebalo bi, koliko god je to moguće, da se pridržavate pravila jezika i preporučenih najboljih praksi.

Ali da li je to uvek moguće? Ne, i zato imamo sledeći aforizam.

Iako praktičnost pobjeđuje čistotu.

Ovo je nastavak prethodnog aforizma. Iako se preporučuje da se pridržavate pravila jezika, u određenim situacijama je sasvim u redu ne poštovati neke od principa.

Greške nikada ne bi trebalo da prođu tiho.

U Pythonu su greške tokom izvršavanja prilično česte. Kao dobra praksa, uvek treba da rešavate greške, a ne da ih utišavate kao brzo rešenje.

Možete predvideti i primeniti odgovarajuće rukovanje greškama za različite tipove grešaka:

try:  
    # radim ovo
except ErrorType1:
    # uradi nešto
except ErrorType2:
    # uradi nešto drugo
...

Trebalo bi da izbegavate gole i generičke izuzetke. Novije verzije Pythona (od Pythona 3.11) podržavaju ulančavanje izuzetaka i grupe izuzetaka kako bi se omogućilo sofisticiranije rukovanje izuzecima.

Osim ako se izričito ne utišaju.

Ovo sledi prethodni aforizam. Ako dizajn zahteva ili dozvoljava da se greška utiša, onda to treba da se uradi eksplicitno.

Na primer, kada se povezujete sa bazom podataka, možete naići na OperationalError zbog nevažećih informacija o konfiguraciji. Pokušajte da se povežete koristeći prilagođenu konfiguraciju. U slučaju da se pojavi OperationalError, koristite podrazumevanu konfiguraciju i pokušajte da se povežete sa bazom podataka.

try:
   # povezivanje pomoću prilagođene konfiguracije
except OperationalError:
   # povezivanje pomoću podrazumevane konfiguracije

Suočeni sa nejasnoćom, odbijte iskušenje da nagađate.

Ovaj aforizam u „Zen Pythona“ je sam po sebi razumljiv. Kada ste u nedoumici, nemojte nagađati. Pokrenite kod i proverite izlaz. Zatim, u zavisnosti od toga da li imate željeno ponašanje, poboljšajte čitljivost ili modifikujte logiku po potrebi.

Uzmite sledeći jednostavan primer sa nizom Boolean vrednosti:

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

Trebalo bi da postoji jedan – i po mogućnosti samo jedan – očigledan način da se to uradi.

Da bi se izvršio određeni zadatak, trebalo bi da postoji jedan i samo jedan preporučeni „Pythonic“ način da se to uradi. Međutim, za svaki problem možemo imati više rešenja.

Čak i u jednostavnom primeru obrtanja stringova, pogledali smo rekurzivno rešenje, sečenje stringova i metodu join().

Ovo je takođe unutrašnja šala s obzirom na nedoslednu upotrebu em-crtica. Obično koristimo em-crtice bez vodećih i završnih razmaka. Ili ih koristimo i sa vodećim i sa zadnjim razmacima.

Dakle, evo šta možemo zaključiti. Aforizam koji naglašava da treba da postoji jedan i samo jedan „Pythonic“ način da se stvari urade može se napisati na više od dva načina.

Iako taj način možda neće biti očigledan u početku, osim ako niste Holanđanin.

Napisano u šaljivom tonu, ovo se odnosi na Gvida van Rosuma, tvorca Pythona (koji je Holanđanin). Naj“Pythonic“ način da se postigne određeni zadatak je prirodan samo za tvorce Pythona.

Dakle, za programere je potrebno iskustvo i učenje iz iskustva da bi bolje iskoristili karakteristike jezika.

Sada je bolje nego nikad.

Kao i kod nekoliko drugih aforizama u „Zen Pythona“, i ovaj se može tumačiti na nekoliko različitih načina.

Jedno tumačenje je da je kao programer prilično uobičajeno odlagati početak kodiranja projekta. Umesto da čekate da isplanirate najsitnije detalje projekta, bolja je ideja početi sada.

Druga moguća interpretacija je: kod koji se pokreće u konačnom broju koraka, i završava se, često je bolji od koda koji greši i zaglavi se u beskonačnoj petlji.

Iako nikada nije često bolje nego sada.

Čini se da je ovaj aforizam u suprotnosti sa prethodnim. Iako je bolje ne odlagati, ipak bismo trebali razmisliti o problemu i dizajnirati kod u skladu sa tim.

Loša je ideja kodirati modul bez razmišljanja o njemu, sa „mirisima koda“ i anti-uzorcima. Zato što je takav kod teško refaktorisati i primeniti korektivne mere.

Ako je implementaciju teško objasniti, to je loša ideja.

Bilo koja logika, koliko god složena bila, uvek se može primeniti u obliku koji je jednostavan za objašnjenje i razumljiv.

Ako je implementaciju teško objasniti, verovatno postoji neka nepotrebna složenost. Kod se može modifikovati ili refaktorisati tako da ga je lakše pratiti.

Ako je implementaciju lako objasniti, to bi mogla biti dobra ideja.

Ovo je povezano sa prethodnim aforizmom i takođe je sam po sebi razumljiv. Ako se implementacija može objasniti jednostavnim rečima, onda je to verovatno dobra ideja.

Zato što će takav kod, čija se implementacija može opisati jednostavnim rečima, vrlo verovatno biti čitljiv i lak za praćenje, uz minimalnu složenost.

Prostori imena su sjajna ideja – hajde da ih više koristimo!

U Pythonu se objektima u određenom opsegu može pristupiti koristeći njihova imena u njihovom prostoru imena. Na primer, možete kreirati klasu i koristiti je kao šablon za kreiranje instanci klase. Sada će sve promenljive instance biti u prostoru imena instance.

Ovo nam omogućava da koristimo objekte sa istim imenom, bez sukoba, jer se nalaze u različitim prostorima imena. Međutim, trebalo bi da ih koristite samo po potrebi i da osigurate da jednostavnost i čitljivost koda nisu ugroženi.

Zaključak

To je sve za ovaj vodič! Nadam se da vam je ovaj vodič pomogao da razumete kako „Zen Pythona“ naglašava stil koda i dobre prakse kodiranja u Pythonu. Što više kodirate, to ćete biti bolji.

Ako ste zainteresovani da naučite kako da pišete koncizan i čitljiv kod, pročitajte ovaj članak o Python jednolinijskim kodovima.