Come lo Zen di Python può aiutarti a scrivere codice migliore

Vuoi migliorare nello scrivere codice Python? Ecco come lo Zen di Python può aiutarti a muovere i primi passi verso di esso.

Python è semplicissimo da imparare. Ma scrivere codice idiomatico e Pythonic facile da mantenere può essere impegnativo, specialmente per i programmatori principianti. PEP-20 ha introdotto “The Zen of Python”, una poesia di Tim Peters, che delinea l’importanza di scrivere codice Pythonic che aderisce alle migliori pratiche.

Per leggere lo Zen di Python, puoi avviare un REPL Python ed eseguire:

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

Come visto, la maggior parte degli aforismi nello Zen di Python sono autoesplicativi. Alcuni aforismi dovrebbero essere accoppiati con il successivo durante l’interpretazione, mentre altri contraddicono un aforisma precedente. Tuttavia, lo Zen di Python è una lettura divertente, coinvolgente e pratica!

Interpretare lo Zen di Python

È stato proposto che lo Zen di Python abbia 20 principi guida per la programmazione in Python. Tuttavia, finora ci sono solo 19 aforismi. Esaminiamoli.

Il bello è meglio del brutto.

Questo aforisma sottolinea l’importanza di scrivere codice elegante e Pythonic.

Il seguente frammento di codice ha un odore di codice:

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

La funzione:

  • Inizializza un elenco vuoto
  • Ha un ciclo all’interno della funzione che aggiunge elementi alla fine dell’elenco e
  • Infine restituisce un elenco

Sebbene questo sia funzionalmente corretto, non è Pythonic, ed è difficile da mantenere.

Puoi scriverlo in modo molto più elegante usando i generatori. Ecco la funzione generatore equivalente della funzione precedente:

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

O ancora meglio, puoi avere la seguente espressione di comprensione del generatore:

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

Esplicito è meglio di implicito.

Durante la scrittura del codice, non lasciare che altri sviluppatori e utenti indovinino il comportamento implicito o predefinito del codice. Sii esplicito. Prendi l’esempio delle importazioni di caratteri jolly:

from some_module import * # wildcard import
from some_other_module import *

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

Evita il più possibile di utilizzare importazioni con caratteri jolly. Perché non è esplicito e inefficiente. Sii specifico quando importi funzioni e classi da altri moduli:

from some_module import this_function # explicit import

result = this_function() # we now know.

Semplice è meglio che complesso.

Questo aforisma afferma che dovremmo mantenere il codice semplice ed evitare inutili complessità. Ad esempio: potresti voler invertire una stringa e implementare la seguente soluzione ricorsiva:

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

Sebbene funzioni, questa è probabilmente una soluzione troppo ingegnerizzata a questo problema, dato che ci sono modi più semplici e più Pythonic per farlo.

Ecco l’approccio del taglio delle stringhe:

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

Ed ecco l’approccio utilizzando metodi e funzioni integrati:

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

Complesso è meglio che complicato.

Quindi cosa trasmette questo prossimo aforisma nello Zen di Python?

L’inversione di stringhe in Python è un’operazione semplicissima. In pratica, però, potremmo aver bisogno di una logica più complessa. Ecco un esempio abbastanza semplice:

Supponiamo che tu debba connetterti a un database:

  • Dovresti prima analizzare un file di configurazione toml, per recuperare le informazioni di configurazione del database.
  • Il connettore del database deve essere installato.
  • È quindi possibile definire una funzione per connettersi al database, anticipare gli errori di connessione, implementare la gestione degli errori e molto altro.
  • Infine, dopo esserti connesso al database, puoi interrogarlo.

Sebbene sia ancora abbastanza semplice, richiede una logica più complessa rispetto all’inversione di stringhe. Ma questo non significa che debba essere complicato. È comunque possibile utilizzare in modo efficace la funzionalità del codice dei moduli integrati e organizzare il codice in modo che altri sviluppatori possano leggerlo, comprenderlo e contribuire.

Flat è meglio di nidificato.

Una struttura piatta è facile da analizzare e comprendere rispetto a una struttura nidificata. Mentre lavori a un progetto, potresti essere tentato di isolare le funzionalità creando moduli separati. Tuttavia, troppa granularità può essere eccessiva.

Detto questo, spesso potresti dover andare oltre la struttura piatta. Ma anche se hai bisogno di nidificare, mantienilo al minimo.

Ecco un esempio:

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

Raso è meglio che denso.

Se stai appena iniziando il tuo viaggio da sviluppatore, potresti essere tentato di abusare di alcune delle funzionalità del linguaggio. Le comprensioni di elenchi, ad esempio, sono Pythonic, ma solo quando le usi dove sono necessarie.

Guarda la seguente comprensione:

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

La comprensione dell’elenco è troppo densa e difficile da analizzare. In questo caso, l’uso di un ciclo for equivalente con condizionali sarà più leggibile. Significato la comprensione è difficile da comprendere. 🙂

La leggibilità conta.

Dovresti sempre scrivere codice leggibile. Ecco alcuni semplici modi per migliorare la leggibilità del codice:

  • Utilizzo di nomi di variabili descrittivi
  • Aggiunta di docstring per funzioni e classi
  • Codice di commento dove necessario
  • Aggiunta di suggerimenti di tipo per argomenti e tipi restituiti di funzioni

I casi speciali non sono abbastanza speciali da infrangere le regole.

Dovresti, per quanto possibile, aderire alle regole della lingua e alle migliori pratiche consigliate.

Ma è sempre possibile? No, ed è per questo che abbiamo il prossimo aforisma.

Anche se la praticità batte la purezza.

Questa è una continuazione del precedente aforisma. Anche se si consiglia di seguire le regole della lingua, in alcuni casi va benissimo non seguire alcuni dei principi.

Gli errori non dovrebbero mai passare in silenzio.

In Python gli errori di runtime sono abbastanza comuni. Come buona pratica, dovresti sempre gestire gli errori e non metterli a tacere come soluzione rapida.

È possibile prevedere e implementare una gestione degli errori appropriata, per i diversi tipi di errore:

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

Dovresti evitare eccezioni nude e generiche. Le versioni più recenti di Python (da Python 3.11) supportano il concatenamento di eccezioni e i gruppi di eccezioni per eseguire una gestione delle eccezioni più sofisticata.

A meno che non sia esplicitamente messo a tacere.

Questo segue l’aforisma precedente. Se il progetto richiede o consente di silenziare l’errore, allora dovrebbe essere fatto in modo esplicito.

Ad esempio: durante la connessione al database, potresti incorrere in OperationalError a causa di informazioni di configurazione non valide. Prova a connetterti utilizzando la configurazione personalizzata. In caso di OperationalError, utilizzare la configurazione predefinita e provare a connettersi al database.

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

Di fronte all’ambiguità, rifiuta la tentazione di indovinare.

Questo aforisma nello Zen di Python si spiega da sé. In caso di dubbio, non indovinare. Ma esegui il codice e controlla l’output. Quindi, a seconda che tu abbia il comportamento desiderato, migliora la leggibilità o modifica la logica secondo necessità.

Prendi il seguente semplice esempio con una tupla di booleani:

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

Dovrebbe esserci un modo ovvio, e preferibilmente solo uno, per farlo.

Per eseguire un determinato compito, dovrebbe esserci uno e un solo modo Python consigliato per farlo. Tuttavia, per qualsiasi problema, possiamo avere più soluzioni.

Anche nel semplice esempio di inversione di stringhe, abbiamo esaminato una soluzione ricorsiva, l’affettamento di stringhe e il metodo join().

Questo è anche uno scherzo interno dato l’uso incoerente dei trattini. Generalmente usiamo trattini lunghi senza spazi iniziali e finali. Oppure lo usiamo con entrambi gli spazi iniziali e finali.

Quindi ecco cosa possiamo dedurre. L’aforisma che sottolinea che ci dovrebbe essere uno – e solo uno – modo pitonico di fare le cose può essere scritto in più di due modi.

Anche se in quel modo potrebbe non essere ovvio all’inizio a meno che tu non sia olandese.

Scritto con una nota leggera, si riferisce a Guido Van Rossum, il creatore di Python (che è olandese). Il modo (più) Pythonico per svolgere un compito particolare viene naturale solo per i creatori di Python.

Quindi, per gli sviluppatori, è necessaria esperienza, e imparare dall’esperienza, per migliorare nell’attingere alle caratteristiche del linguaggio.

Ora è meglio che mai.

Come con alcuni altri aforismi nello Zen di Python, anche questo può essere interpretato in un paio di modi diversi.

Un’interpretazione è che, come sviluppatore, è abbastanza comune procrastinare l’inizio della codifica di un progetto. Piuttosto che aspettare di pianificare i minimi dettagli del progetto, iniziare ora è un’idea migliore.

Un’altra possibile interpretazione è: il codice che viene eseguito in un numero finito di passaggi e termina è spesso migliore del codice che è difettoso e rimane bloccato in un ciclo infinito.

Anche se spesso non è mai meglio di adesso.

Questo aforisma sembra contraddire il precedente. Sebbene sia meglio non procrastinare, dovremmo comunque riflettere sul problema e progettare il codice di conseguenza.

Codificare un modulo, senza pensarci bene, pieno di odori di codice e anti-pattern è una cattiva idea. Perché tale codice è difficile da refactoring e implementare misure correttive.

Se l’implementazione è difficile da spiegare, è una cattiva idea.

Qualsiasi logica, per quanto complessa possa essere, può sempre essere implementata in una forma semplice da spiegare e facile da capire.

Se l’implementazione è difficile da spiegare, probabilmente c’è qualche complessità inutile. Il codice può essere modificato o rifattorizzato in modo che sia più facile da seguire.

Se l’implementazione è facile da spiegare, potrebbe essere una buona idea.

Questo è correlato all’aforisma precedente ed è anch’esso autoesplicativo. Se l’implementazione può essere spiegata in termini semplici, allora è probabilmente una buona idea.

Perché tale codice la cui implementazione può essere descritta, in termini semplici, è molto probabile che sia leggibile e facile da seguire, con una complessità minima.

Gli spazi dei nomi sono un’ottima idea clacson: facciamone di più!

In Python, è possibile accedere agli oggetti in un ambito specifico utilizzando i loro nomi nel loro spazio dei nomi. Ad esempio, puoi creare una classe e usarla come modello per creare istanze della classe. Ora le variabili di istanza saranno tutte nello spazio dei nomi dell’istanza.

Ciò ci consente di utilizzare oggetti con lo stesso nome, senza conflitti, poiché si trovano in spazi dei nomi diversi. Tuttavia, dovresti usarli solo come richiesto e assicurarti che la semplicità e la leggibilità del codice non siano compromesse.

Conclusione

Questo è tutto per questo tutorial! Spero che questa guida ti abbia aiutato a capire come lo Zen di Python enfatizzi lo stile del codice e le buone pratiche di programmazione in Python. Più codifichi, meglio riuscirai a farlo.

Se sei interessato a imparare a scrivere codice conciso e leggibile, leggi questo articolo su Python one-liner.