PROGRAMMAZIONE:PYTON
2)
Fondamenti
3)
Funzione
4)
Programmazione ad oggetti e roba varia...
5)
Trucchi di PYTHON
6)
E per finire...
Introduzione
al PYTHON
Questo
è un corso rapido di minima sul linguaggio di programmazione PYTHON
...
Per
saperne di più, dai un'occhio alla documentazione sul sito
web http://www.python.org
specialmente il TUTORIAL
Se ti stai chiedendo perchè ti dovrebbe interessare, guarda
la pagina dei PARAGONI,
dove Python è paragonato ad altri linguaggi
Fondamenti
Anzitutto,
pensa al Python come pseudo-codice. È quasi vero. Le
variabili non hanno tipi, nè si dichiarano. Appaiono quando
le assegni, spariscono quando non le usi più. L'assegnazione
si fa con l'operatore =
L'eguaglianza
si controlla con l'operatore ==
Puoi
assegnare più variabili alla volta:
x,y,z
= 1,2,3
first, second = second, first
a = b = 123
I
blocchi sono indicati dall'indentazione, e solo
dall'indentazione. (Nè BEGIN/END nè
graffe.) Alcune comuni strutture di controllo:
if x < 5 or (x > 10 and x < 20):
print "Il
valore è OK."
if x < 5 or 10 < x < 20:
print "Il
valore è OK."
for i in [1,2,3,4,5]:
print
"Iterazione numero", i
x = 10
while x >= 0:
print "x
ancora non negativo."
x = x-1
I
primi due esempi sono equivalenti.
La
variabile indice data nel ciclo for itera sugli
elementi di una list (scritta come nell'esempio). Per fare un
ciclo for "normale" (cioè un ciclo di
conteggio), usa la funzione built-in range().
for value in range(100):
print value
(La
riga che inizia con ""
è un commento, ed è ignorata dall'interprete.)
Ok;
ora ne sai abbastanza per (in teoria) implementare qualsiasi
algoritmo in Python. Aggiungiamo un po' di interazione
elementare con l'utente. Per avere input dall'utente (da un
prompt di testo), usa la funzione built-in input.
x = input("Immettere un numero: ")
print "Il quadrato del numero è",
x*x
La
funzione input mostra il prompt passatole (che può
essere vuoto) e permette all'utente di immettere qualsiasi
valore Python valido. In questo caso ci aspettavamo un numero
-- se viene immesso qualcosa d'altro (ad esempio una stringa),
il programma va in crash. Per evitarlo ci servirebbe un po' di
controllo degli errori. Qui non starò a parlarne; basti dire
che, se vuoi che l'input dell'utente sia preso verbatim come
stringa (così che tutto possa venire immesso), userai invece
la funzione raw_input. Se volessi convertire la
stringa immessa s in un intero, potresti poi usare int(s).
Nota:
Se vuoi leggere una stringa con input, l'utente dovrà
scrivere esplicitamente le virgolette. In Python, le stringhe
possono essere racchiuse fra virgolette semplici o doppie.
Quindi,
abbiamo coperto strutture di controllo, input e output -- ora
ci servono delle belle strutture dati. Le più importanti sono
liste e dizionari. Le liste sono scritte con parentesi quadre,
e possono naturalmente essere annidate:
name = ["Cleese", "John"]
x = [[1,2,3],[y,z],[[[]]]]
Una
delle cose carine delle liste è che puoi accederne gli
elementi separatamente o a gruppi, attraverso indiciamento e
affettamento. L'indiciamento si fa (come in molti altri
linguaggi) appendendo l'indice fra parentesi quadre alla
lista. (Nota che il primo elemento ha indice 0).
print name[1], name[0]
name[0] = "Smith"
L'affettamento
è quasi come l'indiciamento, eccetto che scriverai sia
l'indice d'inizio sia quello di fine del risultato, con un
due-punti (":") a separarli:
x = ["spam","spam","spam","spam","spam","eggs","and","spam"]
print x[5:7]
Nota
che la fine è non-compresa. Se manca uno degli indici,
significa che vuoi tutto in quella direzione. Per esempio list[:3]
significa "ogni elemento dall'inizio di list
sino all'elemento 3, non compreso" (Si potrebbe sostenere
che significa in realtà l'elemento 4, poichè il conteggio
parte a 0... vabbè). list[3:], d'altra parte,
significherebbe "ogni elemento di list, partendo
dall'elemento 3 (compreso) sino all'ultimo, compreso."
Per avere risultati davvero interessanti, puoi usare anche
numeri negativi: list[-3] è il terzo elemento dalla
fine della lista...
A
proposito di indiciamento, potrebbe interessarti sapere che la
funzione built-in len ti dà la lunghezza di una
lista.
Ora,
dunque -- e i dizionari? Per farla semplice, sono come liste,
ma i loro contenuti non sono ordinati. Come si indicizzano
allora? Bè, ogni elemento ha una chiave, cioè un
"nome" che è usato per trovare l'elemento proprio
come in un vero dizionario. Un paio di dizionari d'esempio:
{ "Alice" : 23452532, "Boris" : 252336,
"Clarice" : 2352525,
"Doris" : 23624643}
person = { 'first name': "Robin",
'last name': "Hood",
'occupation': "Scoundrel" }
Ora,
per avere l'occupazione di person, usiamo
l'espressione person["occupation"]. Se
volessimo cambiare il suo cognome, potremmo scrivere:
person['last name'] = "of Locksley"
Semplice,
no? Come le liste, i dizionari possono contenere altri
dizionari. O anche liste. E naturalmente anche le liste
possono contenere dizionari. Quindi, si possono facilmente
fare strutture dati piuttosto avanzate.
Funzione
Prossimo
passo: l'astrazione. Vogliamo dare un nome a un brano di
codice, e chiamarlo con un paio di parametri. In altre parole
-- vogliamo definire una funzione (o "procedura").
È facile. Usa la keyword def così:
def square(x):
return x*x
print square(2)
Per
quelli di voi che lo capiscono: tutti i parametri in Python
sono passati per riferimento (come, ad esempio, in Java). Per
quelli che non lo capiscono: non ve ne preoccupate :)
Python
ha gran copia di cose graziose come gli argomenti con nome e
gli argomenti di default e può gestire un numero variabile di
argomenti a una singola funzione. Per altre informazioni su
questo, vedere il tutorial di Python, sezione
4.7.
Se
sai usare le funzioni in generale, questo è quel che ti serve
sapere su di loro in Python. (Ah sì... la keyword return
termina l'esecuzione della funzione e restituisce il valore
dato.)
Una
cosa che potrebbe esserti utile sapere, però, è che in
Python le funzioni sono valori. Così, se hai una funzione
come square, potresti fare qualcosa come:
queeble = square
queeble(2)
Stampa 4
Per
chiamare una funzione senza argomenti devi ricordarti di
scrivere doit() e non doit. Quest'ultimo,
come abbiamo mostrato, ritorna solo la funzione stessa, come
valore. (Questo vale anche per i metodi degli oggetti... vedi
oltre.)
Programmazione
ad oggetti e roba varia...
Penso
tu sappia come funziona la programmazione a oggetti.
(Altrimenti, questa sezione potrebbe non avere molto senso per
te. Non importa... comincia a giocare senza gli oggetti :).)
In Python si definiscono le classi con la keyword (sorpresa!) class,
così:
class Basket:
def __init__(self,contents=None):
self.contents = contents or []
def add(self,element):
self.contents.append(element)
def print_me(self):
result = ""
for element in self.contents:
result = result + " " + `element`
print "Contiene:"+result
Cose
nuove qui:
Tutti
i metodi (funzioni di un oggetto) ricevono un argomento in più
all'inizio della lista degli argomenti, che contiene l'oggetto
stesso (detto self in questo esempio, il che è
l'abitudine.)
I
metodi si chiamano così: object.method(arg1,arg2).
Alcuni
nomi di metodo, come __init__, sono predefiniti, con
significati speciali. __init__ è il nome del
costruttore della classe, cioè la funzione chiamata quando
crei un'istanza.
Alcuni
argomenti possono essere opzionali e avere un valore di
default (come detto prima, nella sezione sulle funzioni).
Questo si fa scrivendo la definizione come:
def spam(age=32): ...
Qui,
spam può essere chiamata con uno o zero parametri.
Se non ne vengono usati, il parametro age avrà
valore 32.
"Logica
corto circuito". È un dettaglio carino... vedi sotto.
Gli
apostrofi rovesciati convertono un oggetto alla sua
rappresentazione stringa. (Così se element contiene
il numero 1, allora `element` è lo stesso di "1"
mentre 'element' è una stringa letterale.)
Il
segno di addizione + è usato anche per concatenare
liste e stringhe. Le stringhe in realtà sono solo liste di
caratteri (il che significa che potete usare su di esse
indiciamento e affettamento e la funzione len. Ganzo,
eh?)
Nessun
metodo (nè variabile membro) è protetto (o privato o simile)
in Python. L'incapsulamento è praticamente questione di stile
di programmazione. (Se ti serve davvero, ci sono convenzioni
sui nomi che permettono un po' di privacy :)).
Ora,
quella logica corto-circuito...
Tutti
i valori in Python possono essere usati come valori logici.
Alcuni dei più "vuoti", come [], 0,
"" e None rappresentano la falsità
logica, mentre la maggior parte degli altri valori (come [0],
1 o "Hello, world") rappresentano
la verità logica.
Ora,
le espressioni logiche come a and b sono valutate così:
Primo, si controlla se a è vero. Se non lo è, è il
risultato. Se lo è, il risultato è b (che
rappresenterà il valore di verità dell'espressione.) La
logica corrispondente per a or b è: se a è
vero, è il risultato. Se non lo è, il risultato è b.
Questo
meccanismo fa sì che and e or si comportino
come gli operatori booleani che dovrebbero implementare, ma ti
permette anche di scrivere espressioni condizionali semplici e
dolci. Per esempio, l'istruzione:
if a:
print a
else:
print b
potrebbe
essere scritta anche:
print a or b
In
effetti, questo è piuttosto idiomatico in Pyton, quindi
meglio abituarcisi. È quel che facciamo nel metodo Basket.__init__.
L'argomento contents ha un valore di default di None
(che è, fra le altre cose, false). Quindi, per controllare se
ha un valore, potremmo scrivere:
if contents:
self.contents =
contents
else:
self.contents = []
Naturalmente,
ora sai che c'è un modo migliore. E perchè non gli diamo il
valore di default di [] da subito? Perchè a causa di
come funziona Python, questo darebbe a tutti i Basket la
stessa lista vuota come contenuto di default. Appena uno
comincia a riempirsi, tutti conterrebbero gli stessi elementi,
e il default non sarebbe più vuoto... per imparare di più su
questo, dovresti leggere la documentazione e cercare la
differenza fra identità e eguaglianza.
Un
altro modo di fare quanto sopra è:
def __init__(self, contents=[]):
self.contents =
contents[:]
Puoi
indovinare come questo funziona? Invece di usare la stessa
lista vuota dappertutto, usiamo l'espressione contents[:]
per fare una copia. (Semplicemente, affettiamo il tutto.)
Così,
per fare effettivamente un Basket e usarlo (cioè
chiamare qualche metodo su di esso) faremo qualcosa come:
b = Basket(['apple','orange'])
b.add("lemon")
b.print_me()
Ci
sono altri metodi magici oltre a __init__. Uno di
essi è __str__, che definisce che aspetto vuole
avere l'oggetto se viene trattato come stringa. Potremmo
usarlo nel nostro Basket invece di print_me:
def __str__(self):
result =
""
for element in
self.contents:
result = result + " " + `element`
return "Contains:"+result
Ora,
se volessimo stampare il Basket b, potremmo
semplicemente usare:
print b
Bello,
eh?
Le
sottoclassi si fanno così:
class SpamBasket(Basket):
Python
permette eredità multipla, quindi puoi avere varie
superclassi fra parentesi, separate da virgole. Le classi si
istanziano così: x = Basket(). I costruttori, come
dicevo, si fanno definendo la speciale funzione membro __init__.
Supponiamo che SpamBasket abbia un costruttore__init__(self,type).
Allora potresti farne uno così: y = SpamBasket("apples").
Se,
nel costruttore di SpamBasket, ti servisse chiamare
il costruttore di una o più superclassi, potresti chiamarlo
così: Basket.__init__(self). Nota che, oltre ai
normali parametri, devi fornire esplicitamente self,
poichè la __init__ della superclasse non sa con
quale istanza stia trattando.
Per
altre meraviglie sulla programmazione a oggetti in Python,
vedi la sezione
9 del tutorial.
Trucchi
di PYTHON
(Questa
sezione è qua solo perchè penso che sia bella. Decisamente
non è necessario leggerla per iniziare a imparare Python.)
Ti
piacciono i concetti sbalorditivi? Allora, se sei davvero
audace, potresti dare un'occhiata al saggio di Guido van
Rossum sulle metaclassi.
Se però preferisci che il tuo cervello non esploda, potrebbe
bastarti questo trucchetto.
Python
usa spazi di nomi dinamici e non lessicali. Questo significa
che se hai una funzione così:
def orange_juice():
return x*2
...
dove una variabile (in questo caso x) non è connessa
a un argomento e non riceve un valore entro la funzione,
Python userà il valore che essa ha dove e quando la funzione
è chiamata. In questo caso:
x = 3
y = orange_juice()
x = 1
y = orange_juice()
Di
solito, questo è il tipo di comportamento che desideri (benchè
questo esempio sia un po' artificioso - raramente si accede
così alle variabili.) Tuttavia, a volte può essere carino
avere qualcosa di simile a uno spazio di nomi statico, cioè,
memorizzare alcuni valori dall'ambiente nella funzione quando
viene creata. Il modo di fare questo in Python è attraverso
gli argomenti di default.
x = 4
def apple_juice(x=x):
return x*2
Qui,
l'argomento x riceve un valore di default che è
eguale al valore della variabile x nel momento in cui
la funzione viene definita. Quindi, finchè nessuno fornisce
un argomento per la funzione, si comporterà così:
x = 3
y = apple_juice()
x = 1
y = apple_juice()
Così
- il valore di x non è cambiato. Se ci servisse solo
questo, avremmo equivalentemente potuto scrivere:
def tomato_juice():
x = 4
return x*2
o
anche:
def carrot_juice():
return 8
Tuttavia,
il punto è che il valore di x è raccolto
dall'ambiente nel momento in cui la funzione viene definita.
In che modo questo è utile? Facciamo un esempio -- una
funzione che compone altre due funzioni.
Vogliamo
una funzione che lavora così:
from math import sin, cos
sincos = compose(sin,cos)
x = sincos(3)
Dove
compose è la funzione che vogliamo costruire, e x
ha il valore -0.836021861538, cioè sin(cos(3)).
Ora, come facciamo questo?
(Nota
che qui stiamo usando funzioni come argomento... il che è un
trucco già parecchio carino in sè.)
Chiaramente,
compose accetta due funzioni come parametri, e
restituisce una funzione che accetta un parametro. Quindi, uno
scheletro di soluzione potrebbe essere:
def compose(fun1, fun2):
def inner(x):
pass
return inner
Potremmo
essere tentati di scrivere return fun1(fun2(x)) entro
la funzione inner e accontentarci. No, no, no. Questo
si comporterebbe molto stranamente. Immagina il seguente
scenario:
from math import sin, cos
def fun1(x):
return x + "
world!"
def fun2(x):
return "Hello,"
sincos = compose(sin,cos)
x = sincos(3)
Ora,
che valore avrebbe x? Esatto: "Hello,
world". Perchè? Perchè quando compose
viene chiamata, raccoglie i valori di fun1 e fun2
dall'ambiente, non quelli che erano in giro quando fu creata.
Per avere una soluzione funzionante, tutto quel che dobbiamo
fare è usare la tecnica che ho descritto prima:
def compose(fun1, fun2):
def inner(x,
fun1=fun1, fun2=fun2):
return fun1(fun2(x))
return inner
Ora
dobbiamo solo sperare che nessuno passi alla funzione
risultante più di un argomento, perchè questo la romperebbe :).
A proposito, visto che non ci serve il nome inner, ed
esso contiene solo un'espressione, tanto vale fare una
funzione anonima, usando la keyword lambda:
def compose(f1, f2):
return lambda x,
f1=f1, f2=f2: f1(f2(x))
Conciso,
ma chiaro. Devi amarlo :)
(E
se non ne hai capito nulla, non preoccuparti. Almeno spero ti
abbia convinto che Python è più che "solo un linguaggio
di scripting"... :o)
E
per finire...
Giusto
poche cosette vicino alla fine. La maggioranza delle funzioni
e classi utili sono piazzate in moduli, che sono file di testo
contenenti codice Python. Puoi importarli e usarli nei tuoi
programmi. Per esempio, per usare il metodo split del
modulo standard string, puoi fare, o:
import string
x = string.split(y)
Oppure...
from string import split
x = split(y)
Per
altre informazioni sui moduli della libreria standard, dai
un'occhiata a www.python.org/doc/lib
Contengono un mucchio di roba utile.
Tutto
il codice nel modulo/script è eseguito quando viene
importato. Se vuoi che il tuo programma possa essere usato sia
come modulo importabile sia come programma eseguibile,
aggiungi alla fine di esso qualcosa come:
if __name__ == "__main__": go()
Questo
è un modo magico per dire che se questo modulo viene fatto
girare come script eseguibile (cioè, se non stiamo venendo
importati da un altri script), allora va chiamata la funzione go.
Naturalmente, potresti fare qualsiasi cosa in questa posizione
dopo il due-punti... :o)
E
per quelli di voi che vogliono rendere eseguibile uno script
su UN*X, usate la seguente prima riga per farlo funzionare da
solo:
Infine,
breve menzione di un concetto importante: le Eccezioni. Alcune
operazioni (come dividere per zero, o leggere da un file
inesistente) producono una condizione di errore, cioè di
eccezione. Puoi anche farti le tue, sollevandole in momenti
appropriati.
Se
non viene gestita l'eccezione, il tuo programma termina e
stampa un messaggio di errore. Puoi evitarlo con una
istruzione di try/except. Per esempio:
def safe_division(a,b):
try:
return a/b
except
ZeroDivisionError:
return None
ZeroDivisionError
è un'eccezione standard. In questo caso, avresti potuto
controllare se b era zero, ma in molti casi, questa
strategia non è applicabile. Inoltre, se non avessimo la
clausola try in safe_division, cioè se
fosse una funzione rischiosa da chiamare, potremmo ancora fare
qualcosa come:
try:
unsafe_division(a,b)
except ZeroDivisionError:
print "Something
was divided by zero in unsafe_division"
Nei
casi in cui normalmente non ci sarebbe uno specifico problema,
però potrebbe succedere, usare le eccezioni permette di
evitare costosi controlli ecc.
Beh,
questo è tutto. Spero tu abbia imparato qualcosa. Ora vai e gioca.
E ricorda il motto per imparare il Python: "Use the
source, Luke." (Traduzione: leggi tutto il codice su cui
puoi mettere le mani :)) Per cominciare, ecco un esempio.
È il ben noto algoritmo QuickSort di Hoare. Una versione con
sintassi colorizzata è qui.
C'è
una cosa che può meritare di essere detta su questo esempio.
La variabile done controlla se la partition
abbia o meno finito di muovere gli elementi. Quindi, quando
uno dei due cicli interni vuol terminare l'intera sequenza di
scambi, mette done ad 1, poi esce con break.
Perchè i cicli interni usano done? Perchè, quando
il primo ciclo interno finisce con break, se il
prossimo ciclo debba partire o meno dipende dal fatto che il
ciclo principale sia finito, cioè, se done sia stata
o meno posta ad 1:
while not done:
while not done:
while not done:
Una
versione equivalente, forse più chiara, ma nella mia opinione
meno bellina, sarebbe:
while not done:
while 1:
if not done:
while 1:
L'unica
ragione per cui ho usato la variabile done nel primo
ciclo era che mi piaceva conservare la simmetria fra i due.
Così li si potrebbe scambiare e l'algoritmo funzionerebbe
ancora.
Alcuni
altri esempi si trovano sulla pagina dei bocconcini
di Joe Strout.
Ciao
e buon divertimento!!!
Se
non hai capito nulla premi ALT + F4 per la guida avanzata! :o)