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.
from modulo import *
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.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.
f = open("www")
f.read()
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
.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,
from import *
– può modificare le variabili che si stano utilizzando e rovinare il resto del codice. Basta evitare di utilizzarlo.Male:
>>> 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()
>>> 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
Male:
# foo.py
a = 1
# bar.py
from foo import a
if qulacosa():
a = 2 # attenzione: foo.a != a
# foo.py
a = 1
# bar.py
import foo
if qulacosa():
foo.a = 2
except
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:
try:
foo = opne("file") # "open" presenta errori ortografici
except:
sys.exit("Impossibile aprire il file")
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
try:
foo = opne("file")
# sarà cambiato in "open" non appena si esegue
except IOError:
sys.exit("Impossibile aprire il file")
except:
è opportuno, come nel caso di un framework che esegue delle retrochiamate o callback, non vogliamo che nessuna retrochiamata disturbi al framework.Il seguente è un modo di non fare molto popolare:
def get_status(file):
if not os.path.exists(file):
print "File non trovato"
sys.exit(1)
return open(file).readline()
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
def get_status(file):
try:
return open(file).readline()
except (IOError, OSError):
print "File non trovato"
sys.exit(1)
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
try:
status = get_status(log)
except SystemExit:
status = None
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
def get_status(file):
return open(file).readline()
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).
def get_status(file):
fp = open(file)
try:
return fp.readline()
finally:
fp.close()
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:
# arg!
return dir+"/"+file
# meglio
return os.path.join(dir, file)
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
import sys, operator
nums = map(float, sys.argv[1:])
print reduce(operator.add, nums)/len(nums)
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.
if foo.bar()['first'][0] == baz.quux(1, 2)[5:9] and \
calculate_number(10, 20) != forbulate(500, 360):
pass
\
potrebbe causare che la linea non sia del tutto corretta. In questo caso, sarebbe almeno un errore di sintassi.Ma se il codice fosse:
value = foo.bar()['first'][0]*baz.quux(1, 2)[5:9] \
+ calculate_number(10, 20)*forbulate(500, 360)
Di solito è molto meglio utilizzare la continuazione implicita che si da tra le parentesi.
Questa versione è a prova di proiettile:
value = (foo.bar()['first'][0]*baz.quux(1, 2)[5:9]
+ calculate_number(10, 20)*forbulate(500, 360))
Ancora nessun commento