Istruzioni

Blocchi logici

Python segue i principi della programmazione strutturata, quindi istruzioni cicliche ed istruzioni condizionali eseguono blocchi logici ben individuati ed un'istruzione "goto" di salto incondizionato non esiste.

I blocchi logico in Python sono identificati con indentazione (rientro): tutte le istruzioni del blocco hanno davanti lo stesso numero di spazi, ed il blocco finisce quando l'indentazione del blocco cessa.

I blocchi dentro altri blocchi (nested o annidati) hanno ulteriore indentazione rispetto al blocco che li contiene. Si consiglia di usare spazi bianchi e non tabulazione, visto che gli spazi rappresentati dal tasto di tabulazione possono essere diversi a seconda dei computer e dei programmi usati. Per consuetudine si usano 4 spazi bianchi per il rientro.

Assegnazione

Abbiamo gia' visto l'istruzione di assegnazione, che crea un oggetto e gli assegna una variabile, che e' un riferimento all'oggetto.

Esempi:

    a  = 3
    b  = 6.0
    a *= b
    c=(a+b) * 3
a=b=3

Esecuzione condizionale

Il costrutto: "if.. then .. else" che ritroviamo in tutti i linguaggi: se la prima condizione e' vera (quella che segue l'if) allora e' eseguito il primo blocco, altrimenti si prosegue alla condizione successiva; se nessuna e' vera viene eseguita la condizione che segue l'istruzione "else". Le clausole "elif" ed "else" sono opzionali

I blocchi sono delimitati dall'indentazione ed iniziano con due punti ":"

if a==b:
   c=d
   e=f
elif a>b:
   c=f
   e=d
else:
   c=0
   e=0

Nell'esempio successivo abbiamo un caso di clausole if annidate (nested if). In Python bisogna sempre fare molta attenzione ai rientri. Altri linguaggi usano le parentesi, che sono una scelta piu' comune e meno soggetta ad errori.

if a==b:
   c=d
   if e==f:
      k=0
   else:
      k=1
elif a>b:
   c=f
   e=d
else:
   c=0
   e=0

Se c'e' una sola istruzione la si puo' mettere nella stessa linea della condizione:

if a==b: k=0

Istruzione with

L'istruzione with e' inserita con Python 3, ma esiste anche in Python 2.6; e' un modo di definire un oggetto locale ad un blocco e di gestire eccezioni relative all'oggetto. La sintassi e':

with espressione  as variabile:
   ...
   ...

L'espressione produce un oggetto, che deve implementare il "context manager protocol"; ovvero ha funzioni: "__enter__(self)" ed "__exit__(self,type,value,traceback)" ,che sono chiamate alla creazione e distruzione dell'oggetto. Servono per gestire un blocco che e' il contesto in cui l'oggetto e' valido ed eventualmente errori (eccezioni) nel blocco che segue l'istruzione "with".

La variabile e' un riferimento all'oggetto; il riferimento e' valido nel blocco che segue, dopo viene eliminato, assieme all'oggetto. Un esempio classico e' la chiusura automatica di un file:

with open("x.txt") as f:
    data = f.read()
    ....

L'open restituisce un riferimento ad un oggetto file: "f" creato dalla funzione: "open" . Questo, all'apertura del file, esegue una sua funzione "__enter__" . A fine blocco f non e' piu' definito ed il garbage collector di python interviene per eliminarlo. L'oggetto f, al momento della sua eliminazione, chiude il file, con __exit__.

Se, in apertura del file, ci sono errori "__exit__" li gestisce ed in questo caso "f" non e' assegnato ed il blocco non e' eseguito. "__exit__" e' chiamata anche per errori nel blocco che segue l'istruzione "with".

Oltre che per files questo sistema e' usato anche per gestire threads, locks etc. Dal python 3.1 ci sono anche i context manager multipli, con una istruzione with tipo:

with A() as a, B() as b:
   ...statements...

Che e' poi equivalente a:

with A() as a:
   with B() as b:
       ...statements...

Istruzioni cicliche

L'istruzione ciclica di Python e' il ciclo "while", che esegue un blocco finche' la condizione e' vera. La condizione e' testata all'inizio del blocco. Alla fine del blocco, in ogni caso, viene eseguito il blocco alla istruzione "else". Anche le istruzioni "while" possono essere annidate:

while x:
   i+=1
   x-=1
else:
   y=i

questa incrementa i e cala x di uno, fino a che x e' zero (falso) quando questo accade il ciclo termina e viene eseguita l'istruzione all'else

Entro un ciclo ci sono istruzioni particolari che alterano la sequenza:

break       interrompe il ciclo
continue    passa al giro successivo
pass        non fa nulla , ad esempio:

       while 1:pass  # e' un loop infinito

Se, nel blocco del while, viene eseguita una istruzione break si esce dal ciclo senza eseguire il blocco all'else

Iterabili ed iteratori

Anche qui abbiamo istruzioni cicliche, ma con una logica diversa.

Liste, dizionari, tuple, sets hanno la caratteristica di essere "iterabili" ( iterable ). Un insieme di oggetti e' iterabile se, su di esso, si puo' definire un oggetto che, in sequenza, assume un diverso valore fra quelli dell'insieme. Questo oggetto che "itera" sulla sequenza e' detto "iteratore".

Un iteratore funziona anche su oggetti complessi ed eterogenei, a differenza di un indice intero, o di un indirizzo, che puo' essere usato in un ciclo solo per descrivere una sequenza di oggetti tutti delle stesse dimensioni.

L'istruzione che crea un iteratore e cicla su tutti gli elementi di un insieme e' l'istruzione "for".

for i in [1,2,3,4]:
   k+=i
else:
   print('fine')

Il riferimento "i" assume, in sequenza, i valori della lista, e per ogni valore che "i" assume si esegue il blocco che segue l'istruzione "if". A fine blocco viene eseguito il blocco della istruzione "else", a meno che un'istruzione break non interrompa il ciclo.

Un ciclo for su un dizionario restituisce le chiavi, nell'esempio che segue viene stampato 'a', poi 'b', infine 'c'

D={'a':0,'b':1,'c':3}
for i in D:
   print(i)

Per iterare su una coppia (chiave, valore) di un dizionario bisogna utilizzare una tupla:

for (key, value) in D.items():
   print(key, '=>', value)

Nel caso seguente iteriamo su una tupla ed usiamo una tupla come iteratore:

T = [(1, 2), (3, 4), (5, 6)]
for (a, b) in T:
     print(a, b)

Per avere un ciclo su numeri interi, come in FORTRAN o C, si utilizza l'oggetto "range", oppure la funzione "enumerate":

for i in range(3):    # produce la sequenza: 0,1,2
     print(i)


for i,a in enumerate(['a','b','c']):
     k[i]=a

"enumerate" restituisce una tupla (numero crescente, elemento dell'enumerable) nello specifico si assegnano valori sia ad "i" che ad "a" ; ai diversi giri abbiamo per la tupla con "i" ed "a":

(1,'a') ; (2,'b') ; (3,'c')

Funzioni per iterabili

Ci sono diverse funzioni, che fanno parte del linguaggio Python (funzioni builtin), utili per lavorare con iterabili.

La funzione list crea un lista, a partire da una serie di valori, o da un iterabile.

La funzione dict produce un dizionario, a partire da una sequenza di tuple di 2 elementi: (chiave,valore).

L'oggetto range rappresenta una sequenza di interi, il primo argomento e' il primo valore, il secondo l'ultimo (escluso), il terzo e' il passo.

La funzione map applica una funzione ad un iterabile, produce un oggetto iterabile che si puo' trasformare in una lista. In Python 2 produceva una lista.

a=map(abs,[-1,2,-3])

list(a)      vale: [1, 2, 3]

La funzione zip unisce a 2 a 2 due iterabili e crea un oggetto iterabile che puo' poi essere tresformato i una lista di tuple. In Python 2 zip produceva una lista.

z = zip((1, 2, 3), (10, 20, 30))

list(z) vale:  [(1, 10), (2, 20), (3, 30)]

La funzione filter applica ad un iterabile una funzione logica elemento per elemento e tiene solo gli elementi per cui la funzione e' True:

list( filter((lambda x: x>0 ),[1,2,3,-1]) ) produce [1, 2, 3]

"lambda" e' un modo di definire una funzione in un unica istruzione, come vedremo dopo.

La funzione iter crea un iteratore per un iterable:

r=[1,2,3]
i=iter(r)

next(i)

"next" alle chiamate successive produce: 1 ,2 , 3 se lo si chiama ancora, dopo che e' arrivato a 3, da errore

Negli esempi della tabella che segue 's' indica un iterabile.

Funzione Effetto
all(s) True se tutti gli elementi sono veri
any(s) True se qualche elemento e' vero
list(1,2,3) produce la lista [1,2,3]
list(range(3)) produce la lista [0,1,2]
list(range(2,10,2)) produce: [2, 4, 6, 8]
list(map(abs,[-1,0,2])) produce: [1, 0, 2]
tuple(map(abs,[-1,0,2])) produce: (1, 0, 2)

Funzioni exec ed eval

La funzione "exec" permette di eseguire una stringa come istruzione Python:

exec('e=3')      # e , che non era definito, diventa 3

La funzione "eval" valuta un'espessione Python, quelle che possono stare a destra di una assegnazione, e ne ritorna il risultato:

eval('abs(-3)+2')  # produce il valore 5

Istruzione print

L'istruzione print non esiste in Python 3. In Python 2 print era un'istruzione, con Python3 diventa la funzione print().

La funzione print accetta un numero variabile di argomenti di tipo diverso e li stampa, usando, per la rappresentazione testuale degli oggetti, la funzione str() degli oggetti stessi.

Print ha anche argomenti opzionali con valori di default, che specificano il file su cui si stampa, il separatore fra gli oggetti stampati, ed il carattere da mettere alla fine della stampa. Di default usa uno spazio per separare gli argomenti, 'n' (il carattere di fine linea) a fine stampa e come file si usa l'output standard del sistema. La sintassi completa per stampare 3 oggetti a,b,c sarebbe quindi:

print(a,b,c,sep=' ',end='n',file=sys.stdout)

La sintassi degli argomenti per le chiamate a funzioni verra' ad ogni modo spiegata in modo dettagliato piu' avanti.