Hacking istantaneo
Impara a programmare con Python

di Magnus Lie Hetland
tradotto da Alex Martelli





Nota: Lavori in corso.

Questa è una breve introduzione all'arte della programmazione, con esempi scritti nel linguaggio di programmazione Python. (Se sai già programmare, ma vuoi una breve introduzione a Python, controlla l'articolo Python Istantaneo.)

(Lodi per l'articolo. Pagina Python di M. L. Hetland)

1. L'ambiente
Per programmare in Python, devi avere istallato l'interprete. Esiste per quasi tutte le piattaforme (compresi Macintosh, Linux e Windows). Altre informazioni su questo sono al sito web di Python. Serve anche un programma editore di testi (come emacs, notepad o altri simili).

2. Cos'è la programmazione?
Programmare un computer significa dargli un insieme di istruzioni che gli dicono cosa fare. Un programma per computer assomiglia in molti modi a una ricetta, come quelle che usiamo in cucina. Per esempio [1]:

    Insalata Fiesta allo Spam [spalla cotta]

    Ingredienti:

    Condimento:
    1/4 tazza di succo di limone
    1/4 tazza di salsa di soia
    1/4 tazza di acqua
    1 cucchiaio di olio di semi
    3/4 di cucchiaino di cumino
    1/2 di cucchiaino di origano
    1/4 cucchiaino di tabasco piccante
    2 teste d'aglio tritate

    Salad:
    1 lattina (3 etti) di carne in scatola Spam tagliata a strisce
    1 cipolla affettata
    1 peperone verde tagliato a strisce
    Lattuga
    12 pomodorini tagliati a metà

    Istruzioni:

    In una giara a chiusura ermetica, combinare tutti gli
    ingredienti del condimento; scuotere bene. Mettere le
    strisce di Spam in un saacchetto di plastica. Versare il
    condimento sulla Spam. Sigillare il sacchetto; marinare per
    30 minuti in frigorifero. Togliere la Spam dal sacchetto;
    mettere a parte 2 cucchiai del condimento, scaldandoli
    in una padella grande. Aggiungere la Spam, la cipolla, e
    il peperone verde. Cuocere 3-4 minuti, sinchè la Spam
    si scalda. Guarnire 4 piatti con la lattuga. Versare col
    cucchiaio la miscela calda sulla lattuga.  Guarnire
    con i mezzi pomodorini. Sono 4 porzioni.

Naturalmente, nessun computer capirebbe questo... e la maggior parte dei computer non potrebbero fare un'insalata anche se capissero la ricetta. Quindi cosa dobbiamo fare per rendere questo più amichevole per computer? Beh -- sostanzialmente due cose. Dobbiamo (1) parlare in un modo che il computer possa capire, e (2) parlare di cose sulle quali esso può fare qualcosa.

Il primo punto significa che dobbiamo usare un linguaggio -- un linguaggio di programmazione per cui abbiamo un programma interprete, e il secondo punto significa che non possiamo aspettarci che il computer faccia un'insalata -- ma possiamo aspettarci che sommi numeri, scriva cose su schermo ecc.

3. Ciao...
C'è una tradizione nei corsi di programmazione di cominciare sempre con un programma che stampa "Ciao, mondo!" sullo schemo. In Python, questo è semplicissimo:

    print "Ciao, mondo!"

Questo è sostanzialmente come la ricetta di prima (anche se è molto più corto!). Dice al computer cosa fare: stampare "Ciao, mondo!". Facilissimo. E se volessimo che facesse più cose?

    print "Ciao, mondo!"
    print "Addio, mondo!"

Non molto più difficile, vero? E non molto interessante... vogliamo poter fare qualcosa con gli ingredienti, come nella insalata di spam. Beh -- che ingredienti abbiamo? Anzitutto, abbiamo stringhe di testo, come "Ciao, mondo!", ma abbiamo anche numeri. Supponiamo di volere che il computer calcoli per noi l'area di un rettangolo. Potremmo dargli la seguente ricettina:

    # Area di un Rettangolo

    # Ingredienti:

    larghezza = 20
    altezza = 30

    # Istruzioni:

    area = larghezza*altezza
    print area

Vedi probabilmente la somiglianza (leggera) con la ricetta dell'insalata di spam. Ma come funziona? Anzitutto, le righe che iniziano con # si chiamano commenti e vengono in effetti ignorate dal computer. Tuttavia, inserire piccole spiegazioni come queste può essere importante per rendere i tuoi programmi più leggibili per gli umani.

Le righe che assomigliano a foo = bar si chiamano assegnamenti. Nel caso di larghezza = 20 dichiamo al computer che la larghezza è 20 da questo punto in avanti. Cosa significa che "la larghezza è 20"? Significa che una variabile di nome "larghezza" viene creata (o se esiste già, viene riusata) e riceve il valore 20. Così, quando usiamo la variabile più avanti, il computer conosce il suo valore. Dunque,

    larghezza*altezza

è sostanzialmente lo stesso che

    20*30

espressione che viene calcolata, dando 600, valore che è poi assegnato alla variabile col nome "area". L'ultima istruzione del programma stampa il valore della variabile "area", quindi quello che vedi eseguendo questo programma è semplicemente

    600

Nota: In alcuni linguaggi devi dire al computer sin dall'inizio del programma di quali variabili avrai bisogno (come gli ingredienti dell'insalata) - Python, invece, è così furbo da scoprire questo man mano che procede.

4. Feedback
OK. Ora puoi eseguire calcoli semplici, o anche piuttosto avanzati. Per esempio, potresti fare un programma per calcolare l'area di un cerchio invece che di un rettangolo:

    raggio = 30

    print raggio*raggio*3.14

Ma questo non è davvero più interessante del programma del rettangolo. Almeno non nella mia opinione. È abbastanza poco flessibile. E se il cerchio che ci interessa avesse un raggio di 31? Come farebbe il computer a saperlo? È un po' come la parte della ricetta dell'insalata che dice "Cuocere 3-4 minuti, sinchè la Spam si scalda". Per sapere quando è cotta, dobbiamo controllare. Ci serve feedback, ovvero input. Come fa il computer a sapere il raggio del nostro cerchio? Anche a lui serve input... quel che possiamo fare è dirgli di chiedere il raggio:

    raggio = input("Quant'è il raggio?")

    print raggio*raggio*3.14

Così le cose cominciano a farsi belle... input è una cosa chiamata funzione. (Imparerai presto a crearti le tue. input è una funzione pre-costruita [built-in] del linguaggio Python). Scrivere soltanto

    input

non farebbe molto... devi mettere un paio di parentesi alla fine. Quindi input() funzionerebbe - aspetterebbe soltanto che l'utente immetta il raggio. La versione di prima è forse più amichevole, però, visto che prima stampa una domanda. Quando mettiamo qualcosa come la stringa di domanda "Quant'è il raggio?" fra le parentesi di una chiamata a funzione, questo si chiama passare un parametro alla funzione. La cosa (o le cose) fra le parentesi è (o sono) il (i) parametro(i). In questo caso passiamo come parametro una domanda così che input sappia cosa emettere prima di ottenere la risposta dall'utente.

Ma come fa la risposta ad arrivare alla variabile raggio? La funzione input, quando è chiamata, restituisce un valore (come molte altre funzioni). Non sei obbligato a usare questo valore, ma, nel nostro caso, è quel che vogliamo. Quindi, le due istruzioni seguenti hanno significati molto differenti:

    foo = input

    bar = input()

foo ora contiene la stessa funzione di input (così che può in effetti essere usata come foo("Quanti anni hai?")) (questo si chiama chiamata a funzione dinamica) mentre bar contiene qualsiasi cosa l'utente abbia immesso.

5. Flusso
Ora possiamo scrivere programmi che eseguono semplici azioni (aritmetica e stampa) e possono ricevere input dall'utente. Questo è utile, ma siamo ancora limitati alla cosiddetta esecuzione sequenziale dei comandi, cioè devono essere eseguiti in ordine fisso. La maggior parte della ricetta dell'insalata di spam è sequenziale o lineare in questo senso. Ma se volessimo dire al computer come controllare la cottura della spam? Se è calda, va rimossa dal forno -- se no, va cotta per un altro minuto circa. Come esprimere questo?

Quel che vogliamo fare è controllare il flusso del programma. Può andare in due direzioni -- togliere la spam, o lasciarla nel forno. Possiamo scegliere, e la condizione è se sia o meno già calda. Questa si chiama esecuzione condizionale. Possiamo fare così:

    temperatura = input("Qual è la temperatura della spam?")

    if temperatura > 50:
        print "L'insalata è cotta a puntino."
    else:
        print "Cuoci ancora un poco l'insalata."

Il significato di questo dovrebbe essere ovvio: se la temperatura è maggiore di 50 (gradi centigradi), stampa un messaggio che dice all'utente che è cotta, altrimenti, dì all'utente di cuocere l'insalata ancora un poco.

Nota: Il rientro ("indent") è importante in Python. I blocchi di esecuzione condizionale (e i cicli e le definizioni di funzione - vedi oltre) devono essere rientrati (e ogni loro riga dev'essere rientrata dello stesso numero di spazi) così che l'interprete sappia dove cominciano e finiscono. Ciò rende anche il programma più leggibile per gli umani.

Torniamo ai nostri calcoli d'area. Vedi cosa fa questo programma?

    # Programma calcolo aree

    print "Benvenuti al Programma calcolo aree"
    print "---------------------------------------"
    print

    # Stampa il menu:
    print "Scegliere una forma:"
    print "1  Rettangolo"
    print "2  Cerchio"

    # La scelta dell'utente:
    forma = input("> ")

    # Calcola l'area:
    if forma == 1:
        altezza = input("Immettere l'altezza: ")
        larghezza = input("Immettere la larghezza: ")
        area = altezza*larghezza
        print "L'area è", area
    else:
        raggio = input("Immettere il raggio: ")
        area = 3.14*(raggio**2)
        print "L'area è", area

Cose nuove in questo esempio:

  1. print usato da solo emette una riga vuota
  2. == controlla se due cose sono eguali, ben diverso da =, che assegna il valore sul lato destro alla variabile sulla sinistra. Questa è una distinzione importante!
  3. ** è l'operatore di elevazione a potenza in Python - così, il raggio al quadrato si scrive raggio**2.
  4. print può emettere più di una cosa, basta separarle con virgole. (Nell'output saranno separate da semplici spazi).

Il programma è molto semplice: chiede un numero, che gli dice se l'utente voglia calcolare l'area di un rettangolo o di un cerchio. Poi, usa una istruzione di if (esecuzione condizionale) per decidere quale blocco debba usare per il calcolo dell'area. Questi due blocchi sono essenzialmente gli stessi usati nei precedenti esempi sull'area. Nota come i commenti rendano il codice più leggibile. È stato detto che il primo comandamento della programmazione è "Commentare!". Se non altro, è una buona abitudine da prendere.

Esercizio:
Estendere questo programma aggiungendo il calcolo dell'area di un quadrato, dove basta che l'utente immetta la lunghezza di un lato. Per fare questo ti serve sapere una cosa: se hai più di due scelte, puoi scrivere qualcosa come:

    if foo == 1:
        # Fai qualcosa...
    elif foo == 2:
        # Fai qualcosa d'altro...
    elif foo == 3:
        # Fai qualcosa di differente...
    else:
        # Se tutto il resto fallisce...

Qui elif è un codice misterioso che significa "else if" :). Quindi: se foo è 1, fai qualcosa; se no, se foo è 2, fai qualcos'altro, ecc. Potresti voler aggiungere anche altre opzioni al programma, tipo i triangoli o anche poligoni arbitrari. Decidi tu.

6. Cicli
L'esecuzione sequenziale e i condizionali sono solo due dei tre mattoni fondamentali della programmazione. Il terzo è il ciclo. Nella sezione precedente ho proposto una soluzione per controllare se la spam fosse calda, ma era chiaramente inadeguata. E se la spam non fosse a puntino neppure la prossima volta che la controlliamo? Come potremmo sapere quante volte dobbiamo controllarla? La verità è che non possiamo. E non dovremmo. Dovremmo poter chiedere al computer di continuare a controllare sinchè non ha finito. Come facciamo? Hai indovinato - usiamo un ciclo, ovvero esecuzione ripetuta.

Python ha due tipi di ciclo: cicli while e cicli for. I cicli for sono forse i più semplici. Per esempio:

    for cibo in "spam", "uova", "pomodori":
        print "Io mangio", food

Questo significa: per ciascun elemento della lista "spam", "uova", "pomodori", stampalo. Il blocco entro il ciclo ("corpo" del ciclo) è eseguito una volta per ciascun elemento, e ogni volta l'elemento corrente è assegnato alla variabile cibo (in questo caso). Un altro esempio:

    for numero in range(1,100):
        print "Ciao, mondo!"
        print "Solo", 100-number, "altre volte..."

    print "Ciao, mondo!"
    print "Questa era l'ultima... puff!"

La funzione range restituisce la lista dei numeri nella gamma passatale (compreso il primo, escluso l'ultimo... in questo caso, [1..99]). Quindi, per parafrasare:

Il contenuto del ciclo è eseguito per ogni numero nella gamma dei numeri da (compreso) 1 sino a (escluso) 100. (Quel che poi fanno il corpo del ciclo e le istruzioni successive lo lasciamo come esercizio).

Ma questo non ci aiuta davvero col nostro problema di cucina. Se volessimo controllare lo spam cento volte, sarebbe una soluzione assai graziosa; ma non sappiamo se basta - o se sia troppo. Vogliamo solo continuare a controllare sinchè non è abbastanza caldo (o, sino a che è abbastanza caldo - questione di punti di vista). Quindi, usiamo while:

    # Programma di cottura spam

    # Recupera la funzione sleep
    from time import sleep

    print "Comincia a cuocere la spam. (Torno fra 3 minuti)"

    # Aspetta 3 minuti (cioè, 3*60 secondi)...
    sleep(180)

    print "Sono tornato :)"

    # Quanto caldo è sufficiente?
    caldo_a_sufficienza = 50

    temperatura = input("Quanto è calda la spam? ")
    while temperatura < caldo_a_sufficienza:
        print "Non è abbastanza calda... cuocila ancora un po'..."
        sleep(30)
        temperatura = input("OK. Adesso quanto è calda? ")

    print "È calda a sufficienza - toglila!"

Cose nuove in questo esempio...

  1. Alcune utili funzioni sono memorizzate in moduli e possono essere importate. In questo caso importiamo la funzione sleep (che fa "dormire" il programma per un certo numero di secondi) dal modulo time che è fra quelli acclusi a Python. (Puoi anche farti i tuoi moduli...)

Esercizio 1
Scrivi un programma che legge continuamente numeri scritti dall'utente e li somma sinchè la somma raggiunge 100. Scrivi un altro programma che legge 100 numeri dall'utente e stampa la loro somma.

7. Programmi più grandi - l'astrazione
Se vuoi un sommario dei contenuti di un libro, non leggi tutte le pagine - dai un'occhiata all'indice, giusto? Esso elenca semplicemente gli argomenti principali del libro. Ora - immagina di scrivere un libro di cucina. Molte delle ricette, come "Spam cremoso e maccheroni" o "Torta svizzera di spam" possono contenere cose simili, come spam, in questo caso -- però non vorresti ripetere come fare lo spam in ogni ricetta (vabbè -- non è proprio che si faccia lo spam... ma abbi pazienza per amor dell'esempio :)). Metteresti la ricetta dello spam in un capitolo separato, facendovi semplicemente riferimento nelle altre ricette. Così - invece di scrivere l'intera ricetta ogni volta, ti basterebbe usare un rimando ad un capitolo. Nella programmazione dei computer, questa si chiama astrazione.

Abbiamo già incontrato qualcosa del genere? Sì. Invece di dire al computer esattamente come ottenere una risposta dall'utente (OK, non avremmo davvero potuto farlo... ma non potremmo neppure fare davvero la spam, quindi... :)) usavamo semplicemente input - una funzione. Possiamo farci le nostre funzioni, per usarle per questo tipo di astrazione.

Diciamo che vogliamo trovare il più grande intero che sia meno di un certo numero positivo. Per esempio, dato il numero 2.7, questo sarebbe 2. Questo si chiama spesso il "floor" del dato numero. (Questo si potrebbe fare con la funzione built-in del Python int, ma, ancora, abbiate pazienza...) Come faremmo? Una semplice soluzione sarebbe provare tutte le possibilità da zero:

    numero = input("Quant'è il numero? ")

    floor = 0
    while floor < numero:
        floor = floor+1
    floor = floor-1

    print "Il floor di", numero, "è", floor

Nota che il ciclo termina quando floor non è più minore del numero; aggiungiamo uno ad esso una volta di troppo. Quindi dobbiamo poi sottrarre uno. E se volessimo usare questo 'floor' in una complessa espressione matematica? Dovremmo scrivere l'intero ciclo per ogni numero di cui ci serve il floor. Non molto simpatico... probabilmente hai indovinato cosa faremo invece: metteremo tutto in una nostra funzione, chiamata "floor":

    def floor(numero):
        risultato = 0
        while risultato < numero:
            risultato = risultato+1
        risultato = risultato-1
        return risultato

Cose nuove in questo esempio...

  1. Le funzioni sono definite con la keyword def, seguita dal nome e dai parametri previsti fra parentesi.
  2. Se la funzione deve restituire un valore, questo si fa con la keyword return (che inoltre termina automaticamente la funzione).

Ora che l'abbiamo definita, possiamo usarla così:

    x = 2.7
    y = floor(2.7)

A questo punto, y dovrebbe valere 2. Si possono fare anche funzioni con più di un parametro:

    def somma(x,y):
        return x+y

Esercizio 2
Scrivi una funzione che implementa il metodo di Euclide per trovare il massimo fattor comune fra due numeri. Funziona così:

  1. Hai due numeri, a e b, con a più grande di b
  2. Ripeti quanto segue sinchè b non diventa zero:
    1. a è posto al valore di b
    2. b è posto al resto della divisione di a (prima del cambiamento) per b (prima del cambiamento)
  3. Torni poi l'ultimo valore di a

Suggerimenti:

8. Ancora sulle funzioni
Com'è andato l'esercizio? È stato difficile? Ancora un po' confuso sulle funzioni? Non preoccuparti - non ho ancora terminato di trattare l'argomento.

Il tipo di astrazione che abbiamo usato costruendo funzioni è spesso chiamata astrazione procedurale, e molti linguaggi usano la parola procedura oltre alla parola funzione. I due concetti sono differenti, ma entrambi sono chiamati funzioni in Python (visto che sono definiti e usati allo stesso modo, più o meno).

Qual è la differenza (in altri linguaggi) fra funzioni e procedure? Beh - come hai visto nella sezione precedente, le funzioni possono restituire un valore. La differenza è che le procedure non restituiscono un tale valore. In molti modi, questa idea di dividere le funzioni in due tipi - quelle che in effetti restituiscono un valore, e quelle che non lo restituiscono - può essere assai utile.

Una funzione che non restituisce un valore (una "procedura") è usata come "sotto-programma" ovvero "subroutine". Chiamiamo la funzione, e il programma fa qualcosa, tipo fare la panna montata o che altro. Possiamo usare questa funzione in molti posti senza riscrivere il codice. (Questo è chiamato riuso del codice - ne riparleremo più avanti).

L'utilità di una simile funzione è nei suoi effetti collaterali - essa cambia il proprio ambiente (ad esempio, mescolando lo zucchero alla panna e montandola...). Vediamo un esempio:

    def ciao(chi):
        print "Ciao,", chi

    ciao("mondo")
    # Stampa "Ciao, mondo"

Stampare roba è considerato un effetto collaterale, e poichè questo è tutto che fa quella funzione, è proprio tipico per una cosiddetta procedura. Ma... non cambia davvero il proprio ambiente, no? Come potrebbe farlo? Proviamo:

    # Il modo *sbagliato* di farlo
    eta = 0

    def cambiaEta(a):
        eta = a

    cambiaEta(100)
    print eta
    # Stampa "0"

Cosa c'è di sbagliato qui? Il problema è che la funzione cambiaEta crea la propria variabile locale, che si chiama egualmente eta, ma è vista solo entro cambiaEta. Come possiamo evitarlo? Possiamo usare una cosa chiamata variabili globali.

Nota: le variabili globali non sono molto usate in Python. Esse possono facilmente portare a difetti di struttura, o a quel che si chiama codice-spaghetti. Le uso qui per portare a tecniche più complesse; tu, se appena puoi, evitale.

Dicendo all'interprete che una variabile è globale (con una istruzione come global eta) gli indichiamo in pratica di usare la variabile che è fuori dalla funzione invece di crearne una nuova locale (quindi, è global in contrapposizione a local.) Il programma può dunque essere riscritto così:

    # Modo corretto, ma non molto buono, di farlo
    eta = 0

    def cambiaEta(a):
	global eta
        eta = a

    cambiaEta(100)
    print eta
    # Stampa "100"

Quando imparerai gli oggetti (più avanti), vedrai che un modo più appropriato di fare questo è usare un oggetto con una proprietà eta e un metodo cambiaEta. Nella sezione sulle strutture dati, vedrai qualche esempio migliore di funzioni che cambiano il proprio ambiente.

Beh - e le vere funzioni, allora? Cos'è una funzione, in realtà? Le funzioni matematiche sono una specie di "macchina" che riceve dell'input e calcola un risultato. Torna lo stesso risultato ogni volta, se le si presenta lo stesso input. Per esempio:

    def quadrato(x):
        return x*x

Questo equivale alla funzione matematica f(x)=x2. Si comporta da brava funzione, cioè si appoggia solo sul suo input, e non cambia il proprio ambiente in alcun modo.

Quindi - ho mostrato due modi di fare funzioni: un modo è come una procedura, e non restituisce un risultato; l'altro è più come una funzione matematica e non fa altro che restituire un risultato (o quasi). Naturalmente, è possibile fare qualcosa di intermedio fra questi due estremi, benchè, se una funzione cambia cose, dovrebbe essere reso chiaro che lo fa. Potresti segnalare questo col suo nome, per esempio usando solo un sostantivo per denominare funzioni "pure" come quadrato e un imperativo per funzioni simili a procedure, come cambiaEta.

9. Più ingredienti - strutture dati
Beh - sai già molto: come ottenere input e dare output, come strutturare algoritmi (programmi) complicati, come eseguire aritmetica; eppure il meglio deve ancora venire.

Che ingredienti abbiamo usato nei nostri programmi sinora? Numeri e stringhe. Giusto? Un po' noioso... ora introduciamo un altro paio di ingredienti per rendere le cose un poco più eccitanti.

Le strutture dati sono ingredienti che strutturano i dati (sorpresa, sorpresa...). Un singolo numero non ha molta struttura, no? Ma diciamo che vogliamo più numeri messi assieme come singolo ingrediente - questo avrebbe un po' di struttura. Per esempio, potremmo volere una lista di numeri. Questo è facile:

    [3,6,78,93]

Ho nominato le liste nella sezione sui cicli, ma non ne ho davvero detto molto. Beh - questo è come si fanno. Basta elencare gli elementi, separati da virgole e chiusi fra parentesi quadre.

Saltiamo dritti in un esempio che calcola i numeri primi (cioè divisibili solo per se stessi e 1):

    # Calcola tutti i primi sotto 1000
    # (non il miglior modo di farlo, ma...)

    risultati = [1]
    candidati = range(3,1000)
    base = 2
    prodotto = base

    while candidati:
        while prodotto < 1000:
            if prodotto in candidati:
                candidati.remove(prodotto)
            prodotto = prodotto+base
        risultati.append(base)
        base = candidati[0]
        prodotto = base
        del candidati[0]

    print risultati

Cose nuove in questo esempio...

  1. La funzione built-in range restituisce in realtà una lista che può essere usata come qualsiasi altra lista. (Comprende il primo indice, ma non l'ultimo).
  2. Una lista può essere usata come variabile logica. Se non è vuota, è vera - se è vuota, è falsa. Così, while candidati significa "finchè la lista chiamata candidati non è vuota" cioè più semplicemente "finchè ci sono ancora candidati".
  3. Puoi scrivere if unElemento in unaLista per controllare se un elemento è in una lista.
  4. Puoi scrivere unaLista.remove(unElemento) per togliere unElemento da unaLista.
  5. Puoi concatenare ad una lista un'altra lista usando unaLista.append(unAltraLista). Puoi usare anche + (ad esempio unaLista = unaLista+unAltraLista) ma non è altrettanto efficiente.
  6. Puoi accedere a un elemento di una lista dando la sua posizione come numero (il primo elemento, stranamente, è l'elemento 0) fra parentesi quadre dopo il nome della lista. Così ad esempio unaLista[3] è il quarto elemento della lista unaLista. (Ci torneremo più avanti)
  7. Puoi cancellare variabili usando la keyword del. La puoi anche usare (come facciamo qui) per cancellare elementi da una lista. Così, del unaLista[0] cancella il primo elemento di unaLista. Se la lista fosse [1,2,3] prima della cancellazione, dopo sarebbe [2,3].

Prima di continuare a spiegare i misteri dell'indiciamento degli elementi di liste, spiegherò brevemente l'esempio.

Questa è una versione dell'antico algoritmo noto come "Crivello di Eratostene". Considera un insieme (qui, una lista) di numeri candidati, e sistematicamente ne toglie i numeri che si sa non sono primi. Come lo si sa? Perchè sono prodotti di altri due numeri.

Iniziamo con una lista di candidati che contiene i numeri [2..999] - sappiamo che 1 è primo, e vogliamo tutti i primi sotto 1000. Abbiamo anche una lista detta risultati che in ogni momento contiene i risultati aggiornati sinora. All'inizio, questa lista contiene solo il numero 1. Abbiamo anche una variabile detta base. A ogni iterazione ("giro") dell'algoritmo, togliamo tutti i numeri che sono multipli di questo numero base (che è sempre il più piccolo dei candidati). Dopo ciascuna iterazione, sappiamo che il più piccolo numero restante è primo (poichè tutti i numeri che erano prodotti di numeri più piccoli sono stati rimossi - è chiaro?). Quindi, lo aggiungiamo alla lista dei risultati, poniamo la nuova base a questo numero, e lo togliamo dalla lista dei candidati (per non elaborarlo di nuovo). Quando la lista di candidati è vuota, la lista dei risultati conterrà tutti i primi. Furbo, no?

Cose a cui pensare: cos'ha di speciale la prima iterazione? Qui la base è 2, però anch'essa è rimossa dal "crivello"... perchè? Perchè questo non succede alle altre basi? Possiamo essere sicuri che prodotto è sempre nella lista dei candidati quando vogliamo toglierlo? Perchè?

Ora - e adesso? Ah sì... l'indiciamento. E l'affettamento. Questi sono i modi per accedere ai singoli elementi delle liste Python. Hai già visto il normale indiciamento in azione. È piuttosto semplice. In effetti, ti ho detto tutto quel che hai bisogno di saperne, eccetto una cosa: gli indici negativi contano dalla fine della lista. Quindi, unaLista[-1] è l'ultimo elemento di unaLista, unaLista[-2] è il penultimo, e così via.

L'affettamento, invece, è una novità. È simile all'indiciamento, eccetto che con l'affettamento si può raggiungere un'intera fetta della lista, e non solo un singolo elemento. Come si fa? Così:

    cibo = ["spam","spam","uova","salsicce","spam"]

    print cibo[2:4]
    # Stampa "['uova', 'salsicce']"

[Aggiungerò altro...]

(Ndt: è importante notare - e non è la prima volta che osserviamo questo tipo di comportamento in Python, che è infatti un linguaggio molto coerente...: anche nell'affettamento, il primo elemento indicato è incluso nella lista risultante, il secondo ne è escluso).

10. Più astrazione - oggetti e programmazione orientata agli oggetti
Ecco una frase di gran moda se mai ve ne fu una: "Programmazione orientata agli oggetti".

Come suggerisce il titolo della sezione, la programmazione orientata agli oggetti (detta anche OOP, per "Object Oriented Programming") è solo un altro modo per astrarre dai dettagli. Le procedure astraggono le semplici istruzioni in operazioni più complesse dandogli un nome. Nell'OOP, non trattiamo in questo modo le operazioni, ma gli oggetti. (Ora, questa dev'essere proprio stata una grande sorpresa, hm?). Per esempio, se facessimo un programma per la cottura della spam, invece di scrivere un mucchio di procedure per trattare temperature, tempi, ingredienti ecc, potremmo raccoglierle in un oggetto spam. O forse, potremmo avere anche un oggetto forno e un oggetto orologio... ora, cose come la temperatura sarebbero attributi dell'oggetto spam, mentre il tempo potrebbe essere letto dall'oggetto orologio. E per far sì che il nostro programma faccia qualcosa, potremmo insegnare al nostro oggetto alcuni metodi; per esempio, il forno potrebbe saper cuocere la spam, ecc.

Ora - come fare questo in Python? Beh, non possiamo solo fare un oggetto direttamente. Invece di fare solo un forno, facciamo una ricetta che descrive come sono i forni. Una classe forno molto semplice potrebbe essere:

    class Forno:
        def inserisciSpam(self, spam):
            self.spam = spam

        def ottieniSpam(self):
            return self.spam

Sembra molto strano, no...?

Cose nuove in questo esempio...

  1. Le classi di oggetti sono definite con la keyword class.
  2. I nomi di classe generalmente iniziano con maiuscole, mentre funzioni e variabili (e metodi e attributi) iniziano con minuscole.
  3. I metodi (cioè le funzioni o operazioni che gli oggetti sanno compiere) sono definite al solito modo, ma dentro il blocco della classe.
  4. Tutti i metodi degli oggetti devono avere un primo parametro chiamato self (o qualcosa del genere...). La ragione (spero) ti sarà presto chiara.
  5. Attributi e metodi di un oggetto si accedono così: miaSpam.temperatura = 2, o dilbert.sii_carino().

Penso che alcune cose dell'esempio siano ancora poco chiare. Per esempio, cos'è questa roba di self? E adesso che abbiamo una ricetta per oggetti (cioè una classe), come ne facciamo un oggetto?

Vediamo prima quest'ultima cosa. Un oggetto è creato chiamando il nome della classe come se fosse una funzione:

    mioForno = Forno()

mioForno ora contiene un oggetto Forno, detto di solito una istanza della classe Forno. Supponiamo di avere fatto anche una classe Spam; allora potremmo fare qualcosa come:

    miaSpam = Spam()
    mioForno.inserisciSpam(miaSpam)

mioForno.spam ora conterrebbe miaSpam. Come? Perchè, quando chiamiamo uno dei metodi di un oggetto, il primo parametro, generalmente chiamato self, contiene sempre l'oggetto stesso. (Furbo, eh?) Quindi, la riga self.spam = spam assegna all'attributo spam del corrente oggetto Forno il valore del parametro spam. Nota che sono due cose diverse, anche se si chiamano entrambe spam in questo esempio.

[Continua...]

Risposta all'Esercizio 2
Ecco una versione molto concisa dell'algoritmo:

    def euclide(a,b):
        while b:
            a,b = b,a % b
        return a

Riferimenti

[1]  Ricetta per la Fiesta Spam Salad presa dal Libro Ricette Digitali della Hormel Foods


[Pagina Python Principale]


Copyright © Magnus Lie Hetland (mlh@idi.ntnu.no)
Ultima modifica dell'originale: Thu Oct 21 16:23:29 MET DST 1999