PYTHON: list-set-dictionary comprehension

Le comprehension (comprensione) e’ una forma di scrittura del codice, che serve ad ottimizare la generazione di liste, set e dizionari la sua sintassi e’ la seguente

per le liste:

lista = [ <espressione sulla variabile> for <variabile> in <input_> <filtro opzionale> ]

per le set:

lista = { <espressione sulla variabile> for <variabile> in <input_> <filtro opzionale> }

per i dictionary:

dic = { <espressione(variabile Key): espressione(variabile Value)> for variabile Key,variabileValue in <input_key, input_value> <filtro opzionale> }

Dettagli dei parametri:

  • <espressione sulla variabile> e’ un esperssione matematica o una funzione che agisce sul valore della variabile ricavato dal <input_> il quale puo essere una qualsiasi struttura dati o funzione
  • <input_> struttura o funzione (es. range()) che fa da base dati per la generazione della nuova struttura dati
  • <filtro opzionale> e’ una condizione if che se rispettata scrvie il dato nella nuova struttura, altrimenta la scarta.

Vediamo alcuni esempi :

#genero una set di divisi per due
list1 = [12,13,14,15,16,17,18]
set2 = {x/2 for x in list1}
print(set2)

#genero una lista di divisi per due sono se interi 
list2 = [12,13,14,15,16,17,18]
set3 = [x/2 for x in list2 if x%2 == 0]
print(set3)

#genero un dizionario delle temperature massime per mese
mesi = [('GENNAIO',-1),('FEBBRAIO',-2),('MARZO', 4),('APRILE',9)]
temp_mesi = { m: t for m,t in mesi  }
print(temp_mesi)

#genero un dizionario dei mei con temperature massime sotto 0
temp_mesi_freddi = { m: t for m,t in mesi if t < 0}
print(temp_mesi_freddi)

######risultati 
{6.0, 7.0, 8.0, 7.5, 6.5, 8.5, 9.0}
[6.0, 7.0, 8.0, 9.0]
{'GENNAIO': -1, 'FEBBRAIO': -2, 'MARZO': 4, 'APRILE': 9}
{'GENNAIO': -1, 'FEBBRAIO': -2}

PYTHON: Iteratori Customizzati

Sono Iteratori creati ad-hoc dal programmatore, praticamente sono delle CLASSI che implementano almeno i due metodi __iter__() e __next__()

nel metodo __iter__() Avviene l’inizializzazione dell’iteratore e il suo return (di solito viene ritornato l’oggetto stesso)

nel metodo __next__() ritorna il prossimo elemento nella sequenza e solleva l’eccezione StopIteration alla fine degli elementi

Facciamo un esempio di un iteratore che ad ogni chiamata restituise il valore precedente aumentato di 1.44 partendo da 0

class numeri:
    #inizializzo l'iteratore partendo da 0
    def __iter__(self):
        self.x = 0
        return self

    #calcolo valore successivo restituito ad ogni chimata next(<oggetto>)
    def __next__(self):
        a = self.x
        self.x += 1.44
        return round(a,2)

oggetto = numeri()

#converto in iteratore
test = iter(oggetto)
#scorro iteratore
for i in range(0,100):
    print(next(test))

I commenti al codice dicono tutto, l’unica cosa e’ che questo iteratore non finira’ mai di generare in quanto nella dichiarazione del metodo __next__() non c’e’ limite, pertanto lanciando un for i in range(oggetto) il ciclo durerebbe all’infinito. Se ad esempio volessimo fermare la generazione al numero superiore a 144.00 dovremmo modificare il codice nel modo seguente:

class numeri:
    def __iter__(self):
        self.x=0
        return self
    def __next__(self):
        a = self.x
        self.x +=1.44
        if(a <= 144):
            return round(a,2)
        else:
            StopIteration
    
oggetto = numeri()

test = iter(oggetto)

for i in range (0,120):
    print(next(test))

In fine vediamo un esempio di iteratore customizzato con la possibilita’ di passergli dei parametri (esempio range(0,100):

class numeriDispari(object):
    def __init__(self, max):
        self.max = max #imposto il valore massimo
        self.n = -1 #imposto la partenza che sara sempre 1 
    
    def __next__(self):
        self.n += 2 #incremeto di 2 il valore precedente 
                    ( all inizio self.n = -1, quindi self.n + 2 = 1)
        if self.n > self.max:
            raise StopIteration #se supero il valore max fermo l'iterazione
        else:
            return self.n #altrimenti rimando il valore calcolato
    def __iter__(self):
        return self
    
#stampo i primi 10 numeri dipsari
numeri = numeriDispari(10)
for n in numeri:
    print(n)
#creo una lista dei primi 80 numeri dipari
numeri_d = [ x for x in numeriDispari(100) if x < 80]
print(numeri_d)

I commentio nel codice spiegano il funzionamento

PYTHON: Iterabili e iteratori

In python i concetti iterabile e iteratori possono essere cosi descritti:

Iterabile: Oggetto o struttura dati (liste,touple etc etc) che puo essere trasformato in un iteratore, un altro esempio di iterabile sono le stringhe. l’oggetto iterabile rimane in memoria e puo’ essere usato in qualsiasi momento durante l’uso nel codice

Iteratore: Oggetto o struttura dati che puo’ essere ‘attraversato, scandito’ con i metodi __iter()__ e __next()__ e “ricoda” il suo stato mentre si attraversa. L’oggetto iteratore, una volta “attraversato” viene eliminato e quindi non e’ possibile riutilizzarlo o scorrerlo avanti e indietro. In generale un iteratore non genera i dati che ha dentro di se ma tiene traccia della loro posizione nella fonte da cui li preleva

Facciamo un esempio con una stringa che e’ un oggetto iterabile:

str = "ciao daniel"
print(str)

str_iterabile = iter(str)
print(str_iterabile)

Questo codice restituira’ :

ciao daniel

<str_iterator object at 0x7f421ca85278>

Come vediamo la prima print stampa correttamente la stringa mentre la seconda stampera’ la locazione di memoria dell’oggetto iterabile. per stampare il contenuto dell’oggetto str_iterabile dovremmo usare il metodo next()

str = "ciao daniel"
print(str)

str_iterabile = iter(str)
print(next(str_iterabile))

che restituira’:

ciao daniel
c

Quali sono i principali vantaggi nell’uso di un iteratore?

il principale vantaggio sta’ nel occupare poca memoria nel trattare molti dati, ad esempio la lettura di un file CSV tramite un iteratore legge ed elabora una riga alla volta per poi passare alla successiva ‘dimenticandosi’ dei dati precedenti

PYTHON: generatori

I generatori sono delle speciali funzioni che restituiscono un iterabile, un esempio di generatore e’ la funzione range(x,y,z) usata per generare una sequenza di interi che parte da x fino ad y con passo z.

ad esempio la chiamata range(0,10,1) assegnata al comando print mi crea una lista di valori da 0 a 10 con passo 1

x = range(0,10,1)
print(list(x)

Questo codice restituira una lista [0,1,2,3,4,5,6,7,8,9]

Creiamo adesso un generatore che restituisca una lista di valori calcolati

def generatore(x):
    for y in range(0,x)
    y = y - 1
    yield y

test = generatore(4)
print(list(test))

Questo codice resittuira’ una lista [-1,0,1,2]

Vediamo di capire come funziona. Alla prima chiamata di test = generatore(4) entro nella funzione generatore(x), la funziona mi genera un elenco di naturali che partono da 0 a 4, al primo ciclo y vale 0 -1 = -1, al secondo giro di for y = 1 -1 = 0 e cosi’ via.

Scrittura dei generatori in modalita’ COMPREHENSION

La modalita’ comprehension (compressa) e’ una modalita di scrittura in python molto utilizzata sia nei generatori che nelle creazione di strutture dati tipo liste, touple dizionari etc etc. vediamo ora come scrivere lo stesso generatore di prima in modalita’ comprehension:

test= ( y-1 for y in range(10))
print(list(test))

Queste due righe daranno come risultato lo stesso di quello precedente!

PYTHON: Liste-Touple-set e dizionari

Liste, touple set e dizionari in python sono delle strutture dati iterabili, anche non omogenee, indicizzate o meno che facilitano la gestione dei dati stessi, potendoli organizzare, ordinare o combinare assieme.

Vediamone le differenze principali :

nomesintassidescrizioneesempi
listelista = [x,y,z ….]Sono strutture molto simili agli array in altri linguaggi di programmazione, sono indicizzate partendo da 0 e possono essere modificate in seguito alla loro creazionel1 = [1,2,’ciao’,3.15]
toupletuple = (x,y,z …)Sono come le liste ma non sono modificabili in seguito alla loro creazione, ne nella dimensione ne nei loro contenuto, nascono e muoiono cosi come sonot=(1,4,5.4,”ciao”)
setset = {x,y,z…}I set sono come le liste, possono essere modificate nella dimensione e nel contenuto ma non accettano valori ugualis = {1,2,3,’c’,”radio”}
dizionaridic = {key: val,
key: val …
}
Sono delle strutture non indicizzate, contengono delle coppie chiave:valore sono modificabili ed eterogenee la chiave puo essere numerica o alfanumerica cosi’ come i valori.d = { uno: “primo”,
due: 2,
3 : 3,15
}

Ogni struttura puo contenere al suo interno altre strutture ad esempio una tupla contenuta in una lista :

lista = [1,’c’,(1,3,4,5)]

per accedere ai dati della tupla si scrivera’ : dato = lista[2][0] in questo caso dato conterra’ il valore 1. Chiaramente non potro modificare il valore lista[2][0] essendo una tupla, ma potro modificare il valore lista[0] o lista[1] essendo una lista.

dizionario = { 1: ‘c’, 2: (2,1) }

anche in questop caso potro accedere senza modificare ai valori della toupla nel seguente modo :
dato = dizionario[2][1] la variabile dato conterra’ il valore 1