Python
Istantaneo
Qualcosa
di diverso...
Questo
è un corso rapido di minima sul linguaggio di
programmazione Python. Per saperne di più, dai un'occhio alla
documentazione sul sito web di Python, (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.
Questa
introduzione ha ricevuto molte lodi
da lettori soddisfatti, ed è stata tradotta in vari
linguaggi, fra cui Portoghese,
Russo,
Norvegese, Coreano e Spagnolo.
1)
Fondamenti
2)
Funzione
3)
Programmazione ad oggetti e roba varia...
4)
Trucchi di Python :o)
5)
E per finire...
1.
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.
^
On
Top
2.
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.)
^
On
Top
3.
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:
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:
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:
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.
^
On
Top
4.
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 )
^
On
Top
5.
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)
^
On
Top
|