Questo documento può essere considerato un compagno del tutorial di Python. Viene illustrato come utilizzare Python, e quasi ancora più importante, come non usare Python.

Costrutti del linguaggio che non dovresti usare
Sebbene Python ha relativamente pochi trabocchetti o grattacapi rispetto ad altri linguaggi, contiene ancora alcune costrutti che sono di utilità solo in situazioni molto particolari, o che sono semplicemente pericolosi.
from modulo import *
In definizioni di funzioni
from modulo import * non è valido all’interno di definizioni di funzioni. Anche se molte versioni di Python non verificano questa condizione, continua ad essere invalido; allo stesso modo che se si ha un buon avvocato non ci trasforma in innocente. Non utilizzare mai questa forma. Anche nelle versioni in cui sono accettate, la funzione verrà eseguita molto più lentamente, perché il compilatore non potrebbe essere sicuro quali nomi erano locali e quali globali. In Python 2.1 l’uso di questo costrutto produce avvertimenti e talvolta errori.
A livello di modulo
Anche se from modulo import * è perfettamente valido a livello di modulo, di solito il suo utilizzo è ancora una cattiva idea. In primo luogo perché con l’uso si perde una proprietà importante di Python – quella di sapere dove si definisce ogni nome della classe, semplicemente utilizzando la funzione di ricerca del vostro editor. Inoltre si rischia di incontrare errori in futuro, se uno qualsiasi dei moduli incorpora nuove funzioni o classi.
Una delle domande più orribili che si può trovare nei newsgroup è perché il seguente codice non funziona:
Copia codice

f = open("www")
f.read()
Naturalmente, funziona perfettamente (ammesso che abbiate un file chiamato “www”). Non funziona se abbiamo un from os import * da qualche parte. Il módulo os ha una funzione chiamata open() che restituisce un intero. Anche se a volte può essere utile, sovrascrivere le funzioni predefinite è uno degli effetti collaterali più fastidiosi.
Ricordate di non essere mai sicuro dei nomi che esporta un modulo, importate solo quello che serve – from modulo import nome1, nome2, o tenete tutto nel suo modulo e accedervi solo quando è necessario o import modulo;print modulo.nome.
Quando è appropriato?
Ci sono situazioni in cui l’uso di from modulo import * è opportuno:

  • Nel interprete interattivo. Per esempio lo scrivere from math import * trasforma a Python in una calcolatrice scientifica incredibile.
  • Quando si estende un modulo in C con un modulo in Python.
  • Quando il modulo specifica che è sicuro usare from import *.
exec,
Il termine “senza adorni” si riferisce all’uso senza indicare un dizionario esplicitamente, nel qual caso questi costruzioni valutano il codice nell’ambiente corrente. Questo è pericoloso per le stesse ragioni di from import * – può modificare le variabili che si stano utilizzando e rovinare il resto del codice. Basta evitare di utilizzarlo.
Male:
Copia codice

>>> for name in sys.argv[1:]:
>>>     exec "%s=1" % name
>>> def func(s, **kw):
>>>     for var, val in kw.items():
>>>         exec "s.%s=val" % var  # invalido!
>>> execfile("handler.py")
>>> handle()
Bene:
Copia codice

>>> d = {}
>>> for name in sys.argv[1:]:
>>>     d[name] = 1
>>> def func(s, **kw):
>>>     for var, val in kw.items():
>>>         setattr(s, var, val)
>>> d={}
>>> execfile("handle.py", d, d)
>>> handle = d['handle']
>>> handle()
from modulo import nome1, nome2
Questo è un avvertimento più leggero del precedente, ma ancora così è qualcosa che non si dovrebbe usare se non si ha una buona ragione per farlo. Il motivo per cui questo è una cattiva idea è perché improvvisamente si ha un oggetto che vive in due spazi dei nomi diversi. Quando cambia l’oggetto collegato ad uno spazio dei nomi, l’altro non lo farà, quindi ci sarà una discrepanza tra loro. Ciò si verifica, ad esempio, quando un modulo viene ricaricato, o quando si cambia la definizione di una funzione in fase di esecuzione.
Male:
Copia codice

# foo.py
a = 1
# bar.py
from foo import a
if qulacosa():
    a = 2 # attenzione: foo.a != a
Bene:
Copia codice

# foo.py
a = 1
# bar.py
import foo
if qulacosa():
    foo.a = 2
except
Python ha una clausola except: utilizzato per intercettare tutte le eccezioni. Come in tutti gli altri linguaggi orientati ad oggetti, gli errori in Python sollevano eccezioni, e questo provoca che molti errori d programmazione sembrino errori di runtime e rende difficile il lavoro di depurazione.
Nel codice riportato di seguito possiamo vedere un buon esempio:
Copia codice

try:
    foo = opne("file") # "open" presenta errori ortografici
except:
    sys.exit("Impossibile aprire il file")
La seconda linea genera un’eccezione di tipo NameError, che viene catturato dalla clausola except. Il programma si concluderà, e non avrete idea se questo ha a che fare con il fatto se si può leggere o meno il “file”.
Il seguente esempio è scritto meglio
Copia codice

try:
    foo = opne("file")
    # sarà cambiato in "open" non appena si esegue
except IOError:
    sys.exit("Impossibile aprire il file")
Ci sono alcune situazioni in cui l’uso della clausola except: è opportuno, come nel caso di un framework che esegue delle retrochiamate o callback, non vogliamo che nessuna retrochiamata disturbi al framework.
Eccezioni
Le eccezioni sono una delle caratteristiche molto utile in Python. Si dovrebbe imparare a generarle quando succede qualcosa di inaspettato, e catturarle solo nei posti dove si può fare qualcosa per porvi rimedio.
Il seguente è un modo di non fare molto popolare:
Copia codice

def get_status(file):
    if not os.path.exists(file):
        print "File non trovato"
        sys.exit(1)
    return open(file).readline()
Supponiamo che il file viene eliminato tra l’esecuzione di os.path.exists() e la chiamata open(). Ciò farebbe che l’ultima riga generi un’eccezione di tipo IOError. Lo stesso accadrebbe se il file esiste e ha solo i permessi di lettura. Siccome eseguendo il programma non viene ravvisato alcun errore, il risultato del test sarà soddisfacente, e invierà il codice in produzione. Quindi l’utente si trova con un IOError che non viene catturato e deve fare i conti con una strana traccia di messaggi nello stack.
Ecco un modo migliore
Copia codice

def get_status(file):
    try:
        return open(file).readline()
    except (IOError, OSError):
        print "File non trovato"
        sys.exit(1)
In questa versione ci sono due possibilità, o il file viene aperto e legge la riga (in modo che funzioni anche su connessioni NFS o SMB inaffidabili), o viene visualizzato un messaggio e si interrompe l’esecuzione.

Tuttavia, get_status() assume troppo cose – che sarà usato solo in uno script che non si eseguirà per tanto tempo, e non, per esempio, in un programma che viene eseguito per giorni su un server. Naturalmente, quando si chiama la funzione si potrebbe fare qualcosa di simile

Copia codice

try:
    status = get_status(log)
except SystemExit:
    status = None
quindi utilizzare il meno possibile le clausole except renderà il codice migliore: di solito questi saranno costituiti da un except che cattura tutto in main(), o nelle chiamate interne che devono essere sempre eseguite con successo.
Quindi, la migliore versione sarebbe probabilmente
Copia codice

def get_status(file):
    return open(file).readline()
Il codice che chiama la funzione può gestire l’eccezione, se necessario (ad esempio, se si prova la funzione con più file in un ciclo), o semplicemente lasciare che l’eccezione si propaghi.

L’ultima versione non è molto buona: dovuto ai dettagli di implementazione, il file non verrà chiuso quando viene generata un’eccezione fino al termine del gestore, e non può verificarsi in nessuna implementazione che non si basi su C (come per esempio con Jython).

Copia codice

def get_status(file):
    fp = open(file)
    try:
        return fp.readline()
    finally:
        fp.close()
Uso delle batterie
Di tanto in tanto la gente cerca di riscrivere le cose che sono già nelle librerie standard di Python, e di solito il risultato è scadente. Di solito è meglio utilizzare la vasta libreria predefinita che include Python che reinventare la ruota.
Un modulo molto utile che pochi conoscono è os.path. Questo modulo fornisce l’aritmetica corretta per il percorso al sistema operativo in uso, e in genere sarebbe una scelta migliore rispetto a qualsiasi altra che si possa creare.
Confronta:
Copia codice

# arg!
return dir+"/"+file
# meglio
return os.path.join(dir, file)
Altre funzioni utili in os.path sono basename(), dirname() e splitext().
Ci sono molte altre funzioni incluse di default che la gente sembra non conoscere per qualche motivo: min() e max(), per esempio, possono trovare il valore minimo/massimo di qualunque sequenza i cui elementi possono essere confrontati, ma nonostante questo molte persone scrivono le proprie versioni di min() e max(). Un’altra utile funzione è reduce().

Un uso classico di reduce() è il seguente

Copia codice

import sys, operator
nums = map(float, sys.argv[1:])
print reduce(operator.add, nums)/len(nums)
Questo piccolo script stampa la media di tutti i ultimi numeri dalla riga di comando. La funzione reduce() somma tutti i numeri e il resto è solo un pre e post elaborazione.
Allo stesso modo, osservate che float(), int() e long() accettano tutti gli argomenti di tipo stringa, in modo che può essere utilizzato per analizzare — supponendo che si è disposti ad assumere le eccezioni ValueError che può generare.
Utilizzando la barra rovesciata per continuare le sentenze
Dato che Python interpreta la nuova linea come una dichiarazione di fine sentenza, e poiché è spesso più facile dividere le sentenze in diverse linee, molte persone fanno qualcosa del genere:
Copia codice

if foo.bar()['first'][0] == baz.quux(1, 2)[5:9] and \
   calculate_number(10, 20) != forbulate(500, 360):
      pass
È necessario essere consapevoli che questo è pericoloso: uno spazio perso dopo \ potrebbe causare che la linea non sia del tutto corretta. In questo caso, sarebbe almeno un errore di sintassi.
Ma se il codice fosse:
Copia codice

value = foo.bar()['first'][0]*baz.quux(1, 2)[5:9] \
        + calculate_number(10, 20)*forbulate(500, 360)
 
l’errore sarebbe molto meno evidente.

Di solito è molto meglio utilizzare la continuazione implicita che si da tra le parentesi.
Questa versione è a prova di proiettile:

Copia codice

value = (foo.bar()['first'][0]*baz.quux(1, 2)[5:9]
        + calculate_number(10, 20)*forbulate(500, 360))
Crediti
Traduzione italiana di “Idioms and Anti-Idioms in Python” di Moshe Zadka dallo spagnolo “Modismos y Anti-Modismos en Python” di Raul Gonzalez Duque.