Le basi della programmazione Python

Conosciamo questo potente linguaggio.

Semplice ma funzionale

In questo blog è possibile trovare una spiegazione base e puramente teorica dei linguaggi HTML, CSS, Javascript e PHP. Quindi, perchè non continuare? Oggi vedremo il linguaggio di programmazione Python cercando di dare una idea generale di quello che può concretamente offrire.

Python è un linguaggio di programmazione dinamico orientato agli oggetti utilizzabile per molti tipi di sviluppo software. Offre un forte supporto all'integrazione con altri linguaggi e programmi, è fornito di una estesa libreria standard e può essere imparato in pochi giorni. Molti programmatori Python possono confermare un sostanziale aumento di produttività e ritengono che il linguaggio incoraggi allo sviluppo di codice di qualità e manutenibilità superiori. Quindi è un linguaggio di programmazione ad alto livello, rilasciato pubblicamente per la prima volta nel 1991 dal suo creatore Guido van Rossum, programmatore olandese attualmente operativo in Dropbox.

Python supporta diversi paradigmi di programmazione, come quello object-oriented (con supporto all'ereditarietà multipla), quello imperativo e quello funzionale, ed offre una tipizzazione dinamica forte. È fornito di una libreria built-in estremamente ricca, che unitamente alla gestione automatica della memoria e a robusti costrutti per la gestione delle eccezioni fa di Python uno dei linguaggi più ricchi e comodi da usare.

Comodo, ma anche semplice da usare e imparare. Python, nelle intenzioni di Guido van Rossum, è nato per essere un linguaggio immediatamente intuibile. La sua sintassi è pulita e snella così come i suoi costrutti, decisamente chiari e non ambigui. I blocchi logici vengono costruiti semplicemente allineando le righe allo stesso modo, incrementando la leggibilità e l'uniformità del codice anche se vi lavorano diversi autori.

Python è un linguaggio pseudocompilato: un interprete si occupa di analizzare il codice sorgente (semplici file testuali con estensione .py) e, se sintatticamente corretto, di eseguirlo. In Python, non esiste una fase di compilazione separata (come avviene in C, per esempio) che generi un file eseguibile partendo dal sorgente.
L'esser pseudointerpretato rende Python un linguaggio portabile. Una volta scritto un sorgente, esso può essere interpretato ed eseguito sulla gran parte delle piattaforme attualmente utilizzate, siano esse di casa Apple (Mac) che PC (Microsoft Windows e GNU/Linux). Semplicemente, basta la presenza della versione corretta dell'interprete.

Inoltre, Python è free software: non solo il download dell'interprete per la propria piattaforma, così come l'uso di Python nelle proprie applicazioni, è completamente gratuito; ma oltre a questo Python può essere liberamente modificato e così ridistribuito, secondo le regole di una licenza pienamente open-source.

Queste caratteristiche hanno fatto di Python il protagonista di un enorme diffusione in tutto il mondo, e anche in Italia, negli ultimi anni. Questo perché garantisce lo sviluppo rapido (e divertente) di applicazioni di qualsiasi complessità in tutti i contesti: dal desktop al web, passando dallo sviluppo di videogiochi e dallo scripting di sistema.

Dopotutto, se è vero che Python rappresenta una delle tecnologie principali del core business di colossi come Google (YouTube è pesantemente basato su Python) e ILM, allora probabilmente sarà ottimo anche per le tue necessità.

Infine, Python definisce una modalità standard di accesso ai database, la cosiddetta DB-API attualmente alla v2.0. Sono supportati nativamente diversi database (relazionali o non-relazionali), oltre ad essere disponibili attraverso ODBC. Praticamente è possibile collegarsi con qualsiasi database esistente, tra i principali segnaliamo: Oracle, MySQL, PostgreSQL, SQL e ServerSQLite.

Fonte: Python.it

Dalla teoria alla pratica

Partiamo subito dicendo che ogni file Python si riconosce grazie alla estensione finale .py e ogni qualvolta si inizia a scrivere con questo linguaggio di programmazione bisogna partire con:

#! /usr/bin/phyton

print 'Content-type: text/html'
print ''

Per stampare frasi a video si utilizza la parola chiave print, ad esempio:

print 'Hello World'

Variabili:



anni = 35 

print anni

Concatenare variabili:



saluto = "ciao"

nome = "Giovanni"

print saluto + nome

Vengono utilizzati i doppi apici in modo da far capire a Python che il valore contenuto nella variabile è di tipo testo. Senza l'utilizzo degli apici Python interpretebbe la variabile di tipo numero e verrebbe generato un errore. In pratica non è possibile fare:

number = "5" 
age = 7
print number + age 

...perchè sommerebbe un testo ad un numero. Per ovviare a tale problema si scrive: print int(number) + age In questo si dice a Python che tra le virgolette non c'è un testo bensì un numero intero.

Stringhe:

str = "My name is "
name = "Giovanni"

print str + name

Risultato: My name is Giovanni

Array:



array = ["valore1", "valore2", "valore3"]

print array

Certamente possiamo usare Python per compiti più complessi che sommare due più due. Per esempio, possiamo scrivere i primi numeri della serie di Fibonacci in questo modo:

>>> # serie di Fibonacci:
... # la somma di due elementi è l'elemento seguente
... a, b = 0, 1
>>> while a < 10:
...     print(a)
...     a, b = b, a+b
...
0
1
1
2
3
5
8

Questo esempio introduce diversi aspetti nuovi. La prima riga contiene un assegnamento multiplo: le variabili a e b ottengono simultaneamente i valori 0 e 1. Nell’ultima riga il trucco si ripete, mostrando così che le espressioni nella parte destra sono tutte valutate prima che l’assegnamento abbia luogo. Le espressioni della parte destra sono valutate nell’ordine, da sinistra a destra. Un ciclo while viene eseguito fin quando la condizione (in questo caso, a < 10) resta verificata. In Python, come in C, tutti gli interi tranne lo zero sono «veri». Lo zero è «falso». La condizione può anche riguardare una stringa o una lista, o in effetti qualsiasi sequenza. Tutto ciò che ha lunghezza non-nulla è «vero»; le sequenza vuote sono «false». Il test usato in questo esempio è una semplice comparazione. Gli operatori standard per la comparazione sono gli stessi di C: < (minore di), > (maggiore di), == (uguale a), <= (minore o uguale a), >= (maggiore o uguale a) e != (diverso da). Il corpo del ciclo è rientrato: il rientro è il modo di Python per raggruppare le istruzioni. In modalità interattiva, dovete inserire una tabulazione o degli spazi per ciascuna riga rientrata. In realtà, preparerete le istruzioni più complicate in un editor da programmatore: tutti gli editor validi hanno la funzione di rientro automatico. Quando inserite un’istruzione composta in modalità interattiva, dovete concluderla con una riga bianca per indicare che è terminata, dal momento che il parser non può indovinare quando avete inserito l’ultima riga. Si noti che ciascuna riga all’interno di un blocco deve essere rientrata della stessa misura. La funzione print() scrive il valore del parametro o dei parametri che le passate. È diverso da scrivere semplicemente l’espressione da calcolare (come avete fatto prima nell’esempio della calcolatrice), in quanto print() può gestire più parametri, numeri con la virgola e stringhe. Le stringhe sono stampate senza apici; tra ciascun parametro viene inserito uno spazio, per permettervi di formattare l’output in modo elegante, così:

>>> i = 256*256
>>> print('Il valore di i è', i)
Il valore di i è 65536

Potete usare il parametro keyword «end» per evitare l’inserimento di una riga vuota dopo ciascun output, o per terminare l’output con una stringa diversa:

>>> a, b = 0, 1
>>> while a < 1000:
...     print(a, end=',')
...     a, b = b, a+b
...
0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,

Oltre a while di cui abbiamo già parlato, Python utilizza le consuete istruzioni per il controllo del flusso, comuni a molti linguggi, con qualche peculiarità.

Istruzione if:

Forse l’istruzione più famosa è la if. Per esempio:

>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
...     x = 0
...     print('Negative changed to zero')
... elif x == 0:
...     print('Zero')
... elif x == 1:
...     print('Single')
... else:
...     print('More')
...

Possono esserci nessuna, una o più sezioni elif e la sezione else è opzionale. La parola riservata “elif” è una scorciatoia per «else if», e permette di evitare troppi livelli annidati. Una sequenza if … elif … elif … sostituisce le istruzioni switch o case tipiche di altri linguaggi.

Istruzione for:


Se siete abituati a Pascal o a C, troverete l’istruzione for in Python leggermente diversa. Invece di iterare solo su una progressione aritmetica, come in Pascal, o dare la possibilità di definire sia il passo dell’iterazione sia la condizione d’arresto, come in C, il for di Python itera sugli elementi di una qualsiasi sequenza (una lista, una stringa…), nell’ordine in cui appaiono nella sequenza. Per esempio, ma senza alcun sottinteso omicida:

>>> # Misura la lunghezza di alcune stringhe:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
...     print(w, len(w))
...
cat 3
window 6
defenestrate 12

Il codice che modifica una collezione mentre itera sulla stessa può essere complicato da scrivere correttamente. Di solito è più semplice iterare su una copia della collezione, o crearne una nuova:

# Strategia: iterare su una copia
for user, status in users.copy().items():
    if status == 'inactive':
        del users[user]
        


# Strategia: creare una nuova collezione
active_users = {}
for user, status in users.items():
    if status == 'active':
        active_users[user] = status

La funzione range():


Se dovete iterare su una sequenza di numeri, la funzione predefinita range() è molto comoda. Produce una progressione aritmetica:

>>> for i in range(5):
...     print(i)
...
0
1
2
3
4

Il punto di arresto indicato non fa parte della sequenza generata: range(10) produce dieci valori, che sono anche gli indici corretti per una sequenza di lunghezza 10. Potete far partire l’intervallo da un numero diverso o specificare un incremento, anche negativo. A volte l’incremento è chiamato «il passo»:

range(5, 10)
   5, 6, 7, 8, 9

range(0, 10, 3)
   0, 3, 6, 9

range(-10, -100, -30)
  -10, -40, -70

Per iterare sugli indici di una sequenza, potete combinare le funzioni range() e len() come segue:

>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
...     print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb

In casi del genere, tuttavia, vi conviene usare la funzione enumerate(). Se cercate semplicemente di «stampare» un intervallo, succede una cosa strana:

>>> print(range(10))
range(0, 10)

L’oggetto restituito da range() si comporta in modo simile a una lista, ma in effetti non lo è. In realtà è un oggetto che restituisce l’elemento successivo della sequenza desiderata, quando vi iterate sopra, ma non crea davvero la lista, per risparmiare spazio. Chiamiamo iterabile un oggetto di questo tipo: ovvero, un oggetto adatto a essere usato da funzioni e costrutti che si aspettano qualcosa da cui ottenere via via elementi successivi, finché ce ne sono. Abbiamo visto che l’istruzione for è un costrutto di questo tipo; invece, un esempio di funzione che accetta un iterabile come argomento è sum():

>>> sum(range(4))  # 0 + 1 + 2 + 3
6

Vedremo più in là altri esempi di funzioni che restituiscono degli iterabili, o che accettano iterabili come argomento. Infine, se siete curiosi di sapere come si può ottenere una lista da un range(), ecco la risposta:

>>> list(range(4))
[0, 1, 2, 3]

Le istruzioni break e continue, e la clausola else nei cicli:


L’istruzione break come in C, «salta fuori» dal ciclo for o while più interno in cui è inserita. Le istruzioni di iterazione possono avere una clausola else: questa viene eseguita quando il ciclo termina perché l’iterabile si è esaurito (in un for), o perché la condizione è divenuta «falsa» (in un while); non viene però eseguita quando il ciclo termina a causa di una istruzione break. Per esempio, il ciclo seguente ricerca i numeri primi:

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print(n, 'è uguale a', x, '*', n//x)
...             break
...     else:
...         # il ciclo è finito senza trovare un fattore primo
...         print(n, 'è un numero primo')
...
2 è un numero primo
3 è un numero primo
4 è uguale a 2 * 2
5 è un numero primo
6 è uguale a 2 * 3
7 è un numero primo
8 è uguale a 2 * 4
9 è uguale a 3 * 3

(Sì, questo codice è giusto. Fate attenzione: la clausola else appartiene al ciclo for, non all’istruzione if.) Quando viene usata in un ciclo, la clausola else è più simile alla else di un’istruzione try, piuttosto che a quella di un if. La else di un’istruzione try viene eseguita quando non sono rilevate eccezioni, e allo stesso modo la else di un ciclo viene eseguita quando non ci sono break. L’istruzione continue, anch’essa un prestito dal C, prosegue con la successiva iterazione del ciclo:

>>> for num in range(2, 10):
...     if num % 2 == 0:
...         print("Trovato un numero pari", num)
...         continue
...     print("Trovato un numero dispari", num)
Trovato un numero pari 2
Trovato un numero dispari 3
Trovato un numero pari 4
Trovato un numero dispari 5
Trovato un numero pari 6
Trovato un numero dispari 7
Trovato un numero pari 8
Trovato un numero dispari 9

L’istruzione pass:


L’istruzione pass non fa nulla. Può essere usata quando sintatticamente è richiesta un’istruzione, ma il programma in sé non ha bisogno di fare nulla. Per esempio:

>>> while True:
...     pass  # Blocca in attesa dell'interruzione da tastiera (Ctrl+C)
...
Si usa di solito per creare una classe elementare:

>>> class MyEmptyClass:
...     pass
...

Un altro modo di usare pass è come segnaposto per una funzione o una condizione, quando state scrivendo codice nuovo e volete ragionare in termini più astratti. Il pass verrà ignorato silenziosamente:

>>> def initlog(*args):
...     pass   # Ricordati di implementare questa funzione!
...

Definire le funzioni:


Possiamo creare una funzione che scrive i numeri di Fibonacci fino a un limite determinato:

>>> def fib(n):    # scrive la serie di Fibonacci fino a n
...     """Scrive la serie di Fibonacci fino a n."""
...     a, b = 0, 1
...     while a < n:
...         print(a, end=' ')
...         a, b = b, a+b
...     print()
...
>>> # Adesso chiamate la funzione appena definita:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597


La parola chiave def introduce la definizione di una funzione. Deve essere seguita dal nome della funzione e da una lista di parametri formali tra parentesi. Le istruzioni che compongono il corpo della funzione iniziano nella riga successiva, e devono essere rientrate.

Opzionalmente, la prima istruzione della funzione può essere una stringa non assegnata: questa è la docstring, ovvero la stringa di documentazione della funzione. Esistono strumenti che usano le docstring per generare automaticamente la documentazione online o stampata, o per consentire all’utente di accedervi interattivamente. Includere la documentazione nel vostro codice è una buona pratica e dovrebbe diventare un’abitudine.

L’esecuzione di una funzione produce una nuova tabella dei simboli usati per le variabili locali alla funzione. Più precisamente, tutti gli assegnamenti fatti all’interno della funzione conservano il valore in una tabella dei simboli locale; invece, i riferimenti alle variabili per prima cosa cercano il nome nella tabella locale, quindi nella tabella locale delle eventuali funzioni «superiori» in cui la nostra può essere inclusa, quindi nella tabella dei simboli globali, infine nella tabella dei nomi predefiniti. Di conseguenza è possibile riferirsi a una variabile globale o di una funzione superiore, ma non è possibile assegnarle un valore (a meno di non ricorrere all’istruzione global per le variabili globali, o a nonlocal per quelle delle funzioni superiori).

I parametri reali (gli argomenti 1) di una funzione sono introdotti nella tabella dei simboli locali nel momento in cui la funzione è chiamata. Quindi, gli argomenti sono «passati per valore» (dove però il «valore» è sempre un riferimento all’oggetto, non il valore dell’oggetto). 2 Quando una funzione chiama un’altra funzione, una nuova tabella di simboli è creata per quella chiamata.

La definizione della funzione associa il nome della funzione con l’oggetto-funzione nella tabella dei simboli corrente. L’interprete riconosce l’oggetto a cui punta il nome come un oggetto-funzione definito dall’utente. Anche altri nomi possono puntare al medesimo oggetto-funzione e possono essere usati per accedere alla funzione:

>>> fib
&lgt;function fib at 10042ed0&lbt;
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89

Se avete esperienza con altri linguaggi, potreste obiettare che fib non è una funzione ma una procedura, dal momento che non restituisce un valore. Tuttavia in Python anche le funzioni senza un’istruzione return esplicita restituiscono in effetti un valore, per quanto piuttosto insignificante. Questo valore si chiama None (è un nome predefinito). L’interprete di solito evita di emettere direttamente None in output, quando è l’unica cosa che dovrebbe scrivere. Se volete davvero vedere il None, potete usare la funzione print():

>>> fib(0)
>>> print(fib(0))
None

Non è difficile scrivere una funzione che restituisce una lista di numeri di Fibonacci, invece di scriverla:

>>> def fib2(n):  # restituisce i numeri di Fibonacci fino a n
...     """Restituisce una lista con i numeri Fibonacci fino a n."""
...     result = []
...     a, b = 0, 1
...     while a < n:
...         result.append(a)    # vedi sotto
...         a, b = b, a+b
...     return result
...
>>> f100 = fib2(100)    # chiama la funzione
>>> f100                # scrive il risultato
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Questo esempio, come di consueto, introduce alcuni concetti nuovi:
  • L’istruzione return esce dall’esecuzione della funzione restituendo un valore. Se return non seguito da alcuna espressione, allora restituisce None. Anche uscire dalla funzione senza un return restituisce None.
  • L’istruzione result.append(a) chiama un metodo dell’oggetto-lista result. Un metodo è una funzione che «appartiene» all’oggetto e si può chiamare con la sintassi obj.methodname dove obj è l’oggetto (che potrebbe essere il risultato di un’espressione) e methodname è il nome del metodo che è stato definito nel tipo dell’oggetto. Tipi diversi definiscono metodi diversi. Metodi di tipi diversi possono avere lo stesso nome, senza che ciò produca ambiguità. Potete definire i vostri tipi e i vostri metodi, usando le classi: vedi Classi. Il metodo append() mostrato nell’esempio è definito per gli oggetti-lista: aggiunge un nuovo elemento in coda alla lista. In questo esempio è equivalente a result = result + [a], ma più efficiente.

Altre cose sulla definizione delle funzioni:


È possibile definire le funzioni con un numero variabile di parametri. Ci sono tre modi per fare questo, che si possono combinare tra loro:

- Parametri con valori di default:


Il modo più utile è specificare un valore di default per uno o più parametri. In questo modo è possibile chiamare la funzione con meno argomenti di quelli che la definizione prescriverebbe. Per esempio:

def ask_ok(prompt, retries=4, reminder='Please try again!'):
    while True:
        ok = input(prompt)
        if ok in ('y', 'ye', 'yes'):
            return True
        if ok in ('n', 'no', 'nop', 'nope'):
            return False
        retries = retries - 1
        if retries < 0:
            raise ValueError('invalid user response')
        print(reminder)

Questa funzione può essere chiamata in diversi modi: passando solo l’argomento necessario: ask_ok('Do you really want to quit?') passando anche uno degli argomenti opzionali: ask_ok('OK to overwrite the file?', 2) o passando tutti gli argomenti: ask_ok('OK to overwrite the file?', 2, 'Come on, only yes or no!') Questo esempio introduce anche la parola-chiave in, che testa se una sequenza contiene un certo valore oppure no. I valori di default sono valutati al momento della definizione della funzione, nella tabella dei simboli che ospita la definizione. Quindi questo

i = 5

def f(arg=i):
    print(arg)

i = 6
f()

restituirà 5.

Attenzione: I valori di default sono valutati una volta sola. Questo fa differenza quando il default è un oggetto mutabile come una lista, un dizionario o un’istanza di molte altre classi. Per esempio, questa funzione accumula gli argomenti che le vengono passati in chiamate successive:

def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))

Questo produrrà:

[1]
[1, 2]
[1, 2, 3]

Se non volete che i valori di default siano condivisi tra chiamate successive, potete scrivere la funzione in questo modo:

def f(a, L=None):
    if L is None:
        L = []
    L.append(a)
    return L

- Parametri keyword:


Le funzioni possono essere chiamate anche passando argomenti keyword nella forma kwarg=value. Per esempio, questa funzione

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print("-- This parrot wouldn't", action, end=' ')
    print("if you put", voltage, "volts through it.")
    print("-- Lovely plumage, the", type)
    print("-- It's", state, "!")

prevede un parametro obbligatorio (voltage) e tre opzionali (state, action e type). Questa funzione può essere chiamata in molti modi diversi:

parrot(1000)                                          # 1 arg. posizionale
parrot(voltage=1000)                                  # 1 arg. keyword
parrot(voltage=1000000, action='VOOOOOM')             # 2 arg. keyword
parrot(action='VOOOOOM', voltage=1000000)             # 2 arg. keyword
parrot('a million', 'bereft of life', 'jump')         # 3 arg. posizionali
parrot('a thousand', state='pushing up the daisies')  # 1 posizionale, 1 keyword

Ma tutte queste chiamate invece non sono valide:

parrot()                     # manca un argomento richiesto
parrot(voltage=5.0, 'dead')  # argomento non-keyword dopo un keyword
parrot(110, voltage=220)     # doppio valore per lo stesso argomento
parrot(actor='John Cleese')  # argomento keyword sconosciuto

Nella chiamata di funzione, gli argomenti keyword devono seguire quelli posizionali. Ciascun argomento keyword passato deve corrispondere a uno accettato dalla funzione (actor non è un argomento valido per la funzione parrot), anche se l’ordine non è importante. Questo vale anche per gli argomenti non opzionali (parrot(voltage=1000) è una chiamata valida). Nessun argomento può ricevere un valore più di una volta. Ecco un esempio che non funziona perché viola questa restrizione:

>>> def function(a):
...     pass
...
>>> function(0, a=0)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: function() got multiple values for keyword argument 'a'

Quando compare un parametro finale nella forma **name, questo può ricevere un dizionario (vedi Tipi di mapping - dizionari) che contiene tutti gli argomenti keyword che non corrispondono a un parametro formale. Questo può essere unito a un parametro nella forma *name (che descriviamo nella prossima sezione), che riceve una tupla con tutti gli argomenti posizionali che eccedono quelli indicati nella lista dei parametri. *name deve essere elencato prima di **name. Per esempio, se definiamo una funzione in questo modo:

def cheeseshop(kind, *arguments, **keywords):
    print("-- Do you have any", kind, "?")
    print("-- I'm sorry, we're all out of", kind)
    for arg in arguments:
        print(arg)
    print("-" * 40)
    for kw in keywords:
        print(kw, ":", keywords[kw])

Potrebbe essere chiamata così:

cheeseshop("Limburger", "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           shopkeeper="Michael Palin",
           client="John Cleese",
           sketch="Cheese Shop Sketch")

e naturalmente restituirebbe questo:

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
client : John Cleese
sketch : Cheese Shop Sketch

Si noti che l’ordine in cui sono scritti gli argomenti corrisponde sempre a quello in cui li abbiamo inseriti nella chiamata di funzione.

- Parametri speciali:


Gli argomenti possono essere passati a una funzione Python per posizione, oppure esplicitamente in modo keyword. Per ragioni di leggibilità e performance, è una buona idea regolamentare i modi in cui si possono passare gli argomenti, così che basti solo un’occhiata alla definizione della funzione per capire se i vari elementi sono passati per posizione, per keyword o in entrambi i modi. Una definizione di funzione potrebbe essere così:

def f(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
      -----------    ----------     ----------
        |             |                  |
        |        posizionali o keyword   |
        |                                - solo keyword
         -- solo posizionali

dove / e * sono opzionali. Se vengono usati, questi simboli distinguono il tipo di parametro a seconda di come l’argomento può essere passato alla funzione: solo posizionale, posizione o keyword, solo keyword. Gli argomenti keyword sono detti anche «passati per nome».

Parametri posizionali o keyword:


Se / e * non compaiono nella definizione della funzione, allora gli argomenti possono essere passati per posizione o per nome (keyword).

Parametri posizionali o keyword:


Volendo specificare più in dettaglio, è possibile marcare certi parametri come solo posizionali. Per i parametri solo posizionali, l’ordine in cui sono elencati deve essere rispettato e non possono essere passati per nome. I parametri solo posizionali sono messi prima del segno /, che è usato per separarli logicamente dagli altri parametri. Se non c’è il segno / nella definizione della funzione, allora non ci sono parametri solo posizionali. I parametri che vengono dopo il / possono essere posizionali o keyword, oppure solo keyword.

Parametri solo keyword:


Per marcare i parametri come «solo keyword», indicando quindi che gli argomenti corrispondenti possono essere passati solo per nome, mettete un segno * nella lista dei parametri, subito prima del primo parametro «solo keyword».

Un approfondimento sulle liste:


Il tipo di dato «lista» ha diverse funzionalità ulteriori. Ecco un elenco di tutti i metodi disponibili per gli oggetti-lista:
  • list.append(x) Aggiunge un elemento alla fine della lista. Equivale a a[len(a):] = [x].
  • list.extend(iterable) Estende una lista aggiungendovi tutti gli elementi di un oggetto iterabile. Equivale a a[len(a):] = iterable.
  • list.insert(i, x) Inserisce un elemento alla posizione data. Il primo parametro è l’indice dell’elemento prima del quale sarà inserito il nostro, quindi a.insert(0, x) inserisce all’inizio della lista e a.insert(len(a), x) equivale a a.append(x).
  • list.remove(x) Rimuove il primo elemento della lista che ha valore x. Emette un ValueError se non esiste un elemento con questo valore.
  • list.pop([i]) Rimuove e restituisce l’elemento alla posizione specificata. Se non viene specificato un indice, a.pop() rimuove e restituisce l’ultimo elemento della lista. (Le parentesi quadre intorno alla i nell’elenco dei parametri non significano che dovreste usare quelle parentesi quando chiamate il metodo, ma indicano invece che il parametro è opzionale. Vedrete molto spesso questa notazione nella documentazione della libreria standard di Python.)
  • list.clear() Rimuove tutti gli elementi della lista. Equivale a del a[:].
  • list.index(x[, start[, end]]) Restituisce l’indice (partendo da zero) del primo elemento con valore x. Emette un ValueError se non esiste un elemento con quel valore. I parametri opzionali start e end limitano la ricerca all’interno di una determinata sotto-lista, e sono interpretati come nella notazione per il sezionamento. L’indice restituito è però relativo all’intera lista, non alla sequenza che inizia con start.
  • list.count(x) Restituisce il numero di volte che x appare nella lista.
  • list.sort(key=None, reverse=False) Ordina sul posto gli elementi della lista. I parametri possono essere usati per aggiungere criteri all’ordinamento: si veda la funzione sorted() per il loro uso.
  • list.reverse() Capovolge sul posto gli elementi della lista.
  • list.copy() Restituisce una copia per indirizzo (shallow copy) della lista. Equivale a a[:].
Un esempio che utilizza molti metodi delle liste:

>>> fruits = ['orange', 'apple', 'pear', 'banana', 'kiwi', 'apple', 'banana']
>>> fruits.count('apple')
2
>>> fruits.count('tangerine')
0
>>> fruits.index('banana')
3
>>> fruits.index('banana', 4)  # Il prossimo "banana", dalla posizione 4
6
>>> fruits.reverse()
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange']
>>> fruits.append('grape')
>>> fruits
['banana', 'apple', 'kiwi', 'banana', 'pear', 'apple', 'orange', 'grape']
>>> fruits.sort()
>>> fruits
['apple', 'apple', 'banana', 'banana', 'grape', 'kiwi', 'orange', 'pear']
>>> fruits.pop()
'pear'

Avrete notato che i metodi come insert, remove o sort, che modificano soltanto la lista, non hanno valore di ritorno – ovvero, restituiscono il None di default. 1 Questo è un principio di design che vale per tutte le strutture-dati mutabili in Python. Un’altra cosa da osservare è che non tutti i dati possono essere ordinati o confrontati. Per esempio, [None, 'hello', 10] non può essere ordinato perché gli interi non possono essere confrontati con le stringhe e None non si può confrontare con altri tipi di dato. Inoltre, ci sono alcuni tipi che non hanno un ordinamento predefinito: per esempio, 3+4j < 5+7j non è una comparazione valida.

L’istruzione del:


L’istruzione del consente di rimuovere un elemento da una lista, data la sua posizione anziché il valore. È differente dal metodo pop(), che restituisce il valore dell’elemento rimosso. L’istruzione del può anche essere usata per rimuovere una sezione della lista, o svuotare l’intera lista (come abbiamo già fatto assegnando una lista vuota alla sezione). Per esempio:

>>> a = [-1, 1, 66.25, 333, 333, 1234.5]
>>> del a[0]
>>> a
[1, 66.25, 333, 333, 1234.5]
>>> del a[2:4]
>>> a
[1, 66.25, 1234.5]
>>> del a[:]
>>> a
[]


del può anche eliminare una variabile:
>>> del a

Adesso riferirsi ad a produce un errore, almeno finché non le viene assegnato un nuovo valore. Vedremo in seguito altri possibili usi di del.

Tuple e sequenze:


Abbiamo visto che le liste e le stringhe hanno molte proprietà in comune, come le operazioni di indicizzazione e sezionamento. In effetti sono due esempi del tipo di dato sequenza (si veda Sequenze - liste, tuple, range). Dal momento che Python è un linguaggio in evoluzione, altri tipi di sequenza potrebbero essere aggiunti in futuro. Un altro tipo di sequenza predefinita è la tupla. Una tupla è una serie di valori separati da virgola, per esempio:

    >>> t = 12345, 54321, 'hello!'
>>> t[0]
12345
>>> t
(12345, 54321, 'hello!')
>>> # Le tuple possono essere annidate:
... u = t, (1, 2, 3, 4, 5)
>>> u
((12345, 54321, 'hello!'), (1, 2, 3, 4, 5))
>>> # Le tuple sono immutabili:
... t[0] = 88888
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'tuple' object does not support item assignment
>>> # ma possono contenere oggetti mutabili:
... v = ([1, 2, 3], [3, 2, 1])
>>> v
([1, 2, 3], [3, 2, 1])

Come si può vedere, le tuple in output sono sempre scritte con le parentesi, in modo che le tuple annidate siano leggibili facilmente. Possono essere scritte in input con o senza parentesi, anche se molto spesso le parentesi sono comunque necessarie (se la tupla fa parte di un’espressione più grande). Non è possibile assegnare a un elemento della tupla: tuttavia è possibile creare tuple che contengono oggetti mutabili, come una lista. Anche se le tuple possono sembrare simili alle liste, sono usate in contesti diversi e per scopi diversi. Le tuple sono immutabili e di solito ospitano una collezione di elementi eterogenei, a cui si può accedere tramite «spacchettamento» (vedi oltre) o indici, o anche attributi, nel caso di una namedtuples. Le liste sono mutabili e di solito ospitano elementi omogenei, a cui si accede iterando sulla lista. Le tuple che hanno zero o un elemento pongono un problema di costruzione: la sintassi prevede due piccole stranezze per risolvere questi casi. Le tuple vuote si creano con una coppia di parentesi, senza nulla dentro. Le tuple con un solo elemento hanno una virgola finale (non è sufficiente mettere il valore tra parentesi per creare una tupla). Non è bello da vedere, ma funziona. Per esempio:

    >>> empty = ()
>>> singleton = 'hello',    # <-- notare la virgola finale
>>> len(empty)
0
>>> len(singleton)
1
>>> singleton
('hello',)

L’assegnazione t = 12345, 54321, 'hello!' è un esempio di impacchettamento di tupla: i valori``12345``, 54321 e 'hello!' sono impacchettati insieme nella tupla. L’inverso è anche possibile:

>>> x, y, z = t

Questo si chiama, prevedibilmente, spacchettamento di sequenza, e funziona con tutti i tipi di sequenza, a destra del segno di uguaglianza. Lo spacchettamento richiede che il numero delle variabili sul lato sinistro sia uguale al numero di elementi della sequenza sul lato destro. Si noti che l’assegnamento multiplo è in realtà una combinazione delle due operazioni di impacchettamento e spacchettamento.

Set:


Python ha un tipo di dato per i set. Un set è una collezione non ordinata senza elementi duplicati. Tra gli utilizzi più frequenti vi sono i test di appartenenza e l’eliminazione dei duplicati. I set supportano anche le operazioni matematiche di unione, intersezione, differenza e differenza simmetrica. Per creare un set si può usare la funzione set() o le parentesi graffe. Si noti che per creare un set vuoto occorre usare set(), non {}: questo infatti crea un dizionario vuoto, come vedremo nella prossima sezione. Ecco una breve dimostrazione:

    >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'}
>>> print(basket)                      # i duplicati sono stati rimossi
{'orange', 'banana', 'pear', 'apple'}
>>> 'orange' in basket                 # test di appartenza veloce
True
>>> 'crabgrass' in basket
False

>>> # Dimostra le operazioni sui set con i caratteri di due parole
...
>>> a = set('abracadabra')
>>> b = set('alacazam')
>>> a                                  # caratteri unici in a
{'a', 'r', 'b', 'c', 'd'}
>>> a - b                              # in a ma non in b
{'r', 'd', 'b'}
>>> a | b                              # in a o b o entrambi
{'a', 'c', 'r', 'd', 'b', 'm', 'z', 'l'}
>>> a & b                              # sia in a sia in b
{'a', 'c'}
>>> a ^ b                              # in a o b, ma non in entrambi
{'r', 'd', 'b', 'm', 'z', 'l'}

Analogamente alle list comprehensions, esistono le set comprehensions:

    >>> a = {x for x in 'abracadabra' if x not in 'abc'}
>>> a
{'r', 'd'}

Dizionari:


Un altro utile tipo predefinito in Python è il dizionario (si veda Tipi di mapping - dizionari). I dizionari sono anche chiamati «array associativi» o «memorie associative» in altri linguaggi. A differenza delle sequenze che sono indicizzate con intervalli numerici, i dizionari sono indicizzati con chiavi; le chiavi possono essere di qualsiasi tipo immutabile: stringhe e numeri sono sempre adatti come chiavi. Le tuple possono essere usate come chiavi, se contengono solo stringhe, numeri o altre tuple; se una tupla contiene qualsiasi altro oggetto mutabile, direttamente o indirettamente, allora non può fungere da chiave per un dizionario. Non potete usare le liste come chiavi, dal momento che queste possono essere modificate sul posto con l’assegnamento a un indice, il sezionamento o metodi come append() e extend(). Conviene pensare a un dizionario come a una collezione di coppie chiave: valore, con il requisito che le chiavi devono essere univoche all’interno del dizionario. Una coppia di parentesi graffe crea un dizionario vuoto: {}. Per inizializzare il dizionario è possibile inserire nelle parentesi delle coppie chiave: valore; questo è anche il modo in cui i dizionari sono scritti in output. Le operazioni principali con i dizionari sono: conservare un valore accoppiandolo a una chiave; ed estrarre il valore data la chiave. È inoltre possibile cancellare una coppia chiave: valore con del. Se si accoppia un valore a una chiave già in uso, il vecchio valore viene sovrascritto. Estrarre un valore con una chiave inesistente produce un errore. Usare list(d) su un dizionario restituisce una lista di tutte le chiavi usate nel dizionario, in ordine di inserimento (se le preferite ordinate, potete invece usare sorted(d)). Per sapere se una chiave è presente in un dizionario, usate la parola-chiave in. Ecco un esempio di utilizzo di un dizionario:

    >>> tel = {'jack': 4098, 'sape': 4139}
>>> tel['guido'] = 4127
>>> tel
{'jack': 4098, 'sape': 4139, 'guido': 4127}
>>> tel['jack']
4098
>>> del tel['sape']
>>> tel['irv'] = 4127
>>> tel
{'jack': 4098, 'guido': 4127, 'irv': 4127}
>>> list(tel)
['jack', 'guido', 'irv']
>>> sorted(tel)
['guido', 'irv', 'jack']
>>> 'guido' in tel
True
>>> 'jack' not in tel
False

La funzione dict() costruisce un dizionario da una sequenza di coppie chiave, valore:

    >>> dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
{'sape': 4139, 'guido': 4127, 'jack': 4098}

Inoltre, è possibile usare le dict comprehension per creare dizionari da espressioni arbitrarie che restituiscono coppie chiave: valore:

    >>> {x: x**2 for x in (2, 4, 6)}
{2: 4, 4: 16, 6: 36}

Quando le chiavi sono delle stringhe, è più semplice passare a dict() degli argomenti keyword:

    >>> dict(sape=4139, guido=4127, jack=4098)
{'sape': 4139, 'guido': 4127, 'jack': 4098}

Tecniche di iterazione:


Quando occorre iterare su un dizionario, le chiavi e i valori corrispondenti si possono estrarre contemporaneamente con il metodo items():

    >>> knights = {'gallahad': 'the pure', 'robin': 'the brave'}
>>> for k, v in knights.items():
...     print(k, v)
...
gallahad the pure
robin the brave

Quando si itera su una sequenza, l’indice e il valore corrispondente si possono estrarre contemporaneamente con la funzione enumerate():

    >>> for i, v in enumerate(['tic', 'tac', 'toe']):
...     print(i, v)
...
0 tic
1 tac
2 toe

Per iterare su due o più sequenze contemporaneamente, queste possono essere accoppiate con la funzione zip():

    >>> questions = ['name', 'quest', 'favorite color']
>>> answers = ['lancelot', 'the holy grail', 'blue']
>>> for q, a in zip(questions, answers):
...     print('What is your {0}?  It is {1}.'.format(q, a))
...
What is your name?  It is lancelot.
What is your quest?  It is the holy grail.
What is your favorite color?  It is blue.


Per iterare su una sequenza in ordine inverso, si scrive l’iterazione in avanti e su questa si chiama poi la funzione reversed():

    >>> for i in reversed(range(1, 10, 2)):
...     print(i)
...
9
7
5
3
1

Per iterare su una sequenza in modo ordinato, usate la funzione sorted() che restituisce una nuova lista ordinata, lasciando inalterato l’originale:

    >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana']
>>> for f in sorted(set(basket)):
...     print(f)
...
apple
banana
orange
pear

Talvolta si cerca di modificare la lista mentre ci si sta iterando sopra; è spesso più semplice creare invece una nuova lista:

    >>> import math
>>> raw_data = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
>>> filtered_data = []
>>> for value in raw_data:
...     if not math.isnan(value):
...         filtered_data.append(value)
...
>>> filtered_data
[56.2, 51.7, 55.3, 52.5, 47.8]

Perfetto, questo articolo direi che è stato decisamente lungo ma è un utilissimo trampolino di lancio per tutti coloro che vogliono imparare il linguaggio di programmazione Python. Naturalmente abbiamo visto una minuscola parte di quello che può fare Python, pertanto vi invito ad approfondire il tutto visitando le pagine:

  • https://pytutorial-it.readthedocs.io/it/python3.8/index.html: tutorial completo di questo articolo.
  • La libreria standard: sfogliate questo manuale, che fornisce una documentazione completa (anche se sintetica) per tipi, funzioni e moduli della libreria standard. La distribuzione di Python contiene molte cose. Ci sono moduli per leggere le caselle di posta Unix, ricevere documenti via HTTP, generare numeri casuali, leggere le opzioni della riga di comando, scrivere programmi grafici, comprimere dati e molto altro ancora. Un’occhiata veloce a questa documentazione dovrebbe bastare per farsi un’idea di che cosa è disponibile.
  • Installare i moduli Python spiega come installare moduli aggiuntivi scritti da altri programmatori Python.
  • La guida di riferimento del linguaggio è una spiegazione dettagliata della sintassi e della semantica di Python: è una lettura non facile, ma molto utile come guida del linguaggio in sé.
  • https://www.python.org: il sito web di Python. Contiene codice, documentazione e indicazioni per altri contenuti su Python in giro per il web. Questo sito ha dei mirror in vari posti nel mondo: Europa, Giappone, Australia etc. Il mirror potrebbe essere più veloce del sito principale, dipende dalla vostra posizione geografica.
  • https://docs.python.org: accesso rapido alla documentazione di Python.
  • https://pypi.org: il Python Package Index, un tempo chiamato anche «Cheese Shop» 1, è un indice di moduli creati da programmatori Python e disponibili per il download. Una volta che siete pronti a rilasciare il vostro codice, potete registrarvi qui, in modo che altri possano trovare il vostro lavoro.
  • code.activestate: il «Python Cookbook» è una notevole raccolta di esempi, moduli più complessi e script utili. I contributi più interessanti sono raccolti nel libro omonimo (O’Reilly & Associates, ISBN 0-596-00797-3).
  • http://www.pyvideo.org raccoglie video su Python, da convegni e incontri di user-group.
  • https://scipy.org: il progetto «Scientific Python» comprende moduli per calcoli e manipolazioni performanti su array, oltre a moltissimi moduli di algebra lineare, trasformata di Fourier, risolutori non-lineari, distribuzioni di numeri casuali, analisi statistica e altro ancora.

« Articolo successivo:
Le basi della programmazione Java
» Articolo precedente:
Come nacque il primo sito web?