Tuple, Stringhe, Dizionari, Insieme e Oggetti

Abbiamo visto come con python possiamo definire semplicemente dei numeri (interi, decimali, complessi), delle liste di elementi (che possiamo usare per rappresentare dei vettori) e delle funzioni.

Ci restano da vedere molto velocemente:

  1. tuple
  2. stringhe
  3. dizionari
  4. insiemi
  5. oggetti

Le vediamo brevemente direttamente nel workspace.

Tuple e Liste

Le liste sono sequenze di elementi che non possono essere modificati. Ogni elemento di una tupla può essere di qualsiasi tipo, e gli elementi possono essere tutti di tipi diversi tra loro. Diversamente dalle liste, da una tupla non si possono rimuovere o aggiungere elementi: è un oggetto di tipo read only (come Complex). A differenza delle liste, le tuple sono rappresentate come una sequenza di oggetti racchiusi tra parentesi tonde (per le liste si usa le quadre).


In [ ]:
x=2,4
print(type(x))

In [ ]:
print(x, x[0], x[1])

In [ ]:
x[1] = 4

In [ ]:
a,b = 2,4

In [ ]:
a,b = b,a

In [ ]:
print(a,b)

Operazioni su tuple

Con le tuple è possibile effettuare operazioni di concatenamento, indicizzazione, slicing e repetition.

ESEMPIO:


In [ ]:
t1 = (2, 'ciao', 5)
t2 = (3.14, 987)
# Concatenazione
print('concatenazione:', t1+t2)
# Indicizzazione
print('indicizzazione:',t1[1])
# Slicing
print('slicing:',(t1+t2)[2:])
# Repetition
print('repetition:',t2*3)

NOTA: Per definire una tuple di lunghezza pari a uno, ovvero di un singolo elemento, bisogna usare la strana sintassi (1,): si noti la virgola dopo l'uno.


In [ ]:
a=(1)
b=(1,)
print(type(a), type(b))

ESERCIZIO 1: Scrivere una funzione che prende in input due tuple e restituisce una tupla che contiene gli elementi che sono sia nella prima che nella seconda tupla. Scrivere anche una funzione di test che comprenda qualche caso significativo.


In [ ]:
def Intersect(As, Bs):
    # COMPLETARE
    # COMPLETARE
    return As

def UnitTest():
    # COMPLETARE
    # COMPLETARE
    pass

ESERCIZIO 2: (obiettivo: intuire il vantaggio di poter avere anche oggi non mutabili)

Si scriva una funzione che prenda in input due liste e che rimuove dalla prima lista ogni elemento che compare nella seconda lista. La funzione non ritorna nulla, ma modifica la prima lista data in input.

Si può usare il metodo L.remove(e) che rimuove dalla lista L il primo elemento uguale a e. Per testare se un elemento appartiene ad una lista basta eseguire il controllo e in L:

if 3 in [1,2,3,4,5]:
    print('ok')
else:
    print('failed')

In [ ]:
def RimuoviDuplicati(As, Bs):
    # DA COMPLETARE
    # DA COMPLETARE
    pass

L1 = [2, 4, 2, 5, 6, 6, 3, 2, 9, 4]
L2 = [2, 4, 7]
RimuoviDuplicati(L1, L2)
print('L1 =', L1)

Il problema di aliasing

Si consideri l'esempio seguente, ripreso dal Capitolo 5 del libro di riferimento.


In [ ]:
Techs = ['MIT', 'Caltech']
Ivys = ['Harvard','Yale','Penn']
U1 = [Techs, Ivys]
U2 = [['MIT', 'Caltech'], ['Harvard','Yale','Penn']]
print(U1 == U2)
print(id(U1) == id(U2)) # la funzione id() restituisce l'identificativo unico di un oggetto python

In [ ]:
Techs.append('Standford')
print(U1 == U2)

In [ ]:
U1[0].remove('Standford')
print(Techs)

NOTA: abbiamo due percorsi diversi che portano allo stesso oggetto di tipo lista: il primo attraverso il nome della lista Techs, il secondo attraverso il primo elemento della lista U1. Basta modificare uno dei due, che il cambiamento si riflette sull'altro: si parla in questo caso di side effect, in quanto si potrebbero introdurre degli effetti non desiderati, difficili da individuare. Notare che problemi di questo tipo non si hanno con strutture dati di tipo read only.

Stringhe

Le stringhe sono delle sequenze di caratteri con una implementazione builtin molto efficiente. Anche le stringhe sono degli oggetti di tipo read only e non possono essere modificate. I metodi che operano sulle stringhe, sono delle funzioni che restituiscono delle nuove stringhe.


In [ ]:
commento = "Enrico, stai sereno!"
for c in commento:
    print(c.upper(), end=' ')

Per vedere alcuni metodi utili sulle funzioni, ricordarsi di usare il tab destro per l'autocompletion dopo il nome+punto di una variabile:

commento. '+' <tab>

Esempio:


In [ ]:
#commento.

In [ ]:
# INDICIZZAZIONE E SLICING
print(commento[3], commento[-1], commento[2:5])

In [ ]:
print(commento.lower())

In [ ]:
print(commento.upper())

Da notare che la funzione upper non ha modifcato la stringa commento, ma ha restituito una nuova lista con tutte le lettere maiuscole. La maggior parte delle funzioni standard di python che opera sulle stringhe operano in questo modo. Per maggiori dettagli, potete sempre consultare help(str).


In [ ]:
commento

Metodi utili sulle stringhe

I seguenti metodi sono tutti molto utili e restituiscono delle nuove stringhe lasciando la stringa iniziale immutata:

  • s.count(s1): conta qualche volte la stringa s1 è contenuta in s
  • s.find(s1): restituisce l'indice della stringa s in cui ha trovato per la prima volta la stringa s1; altrimenti restituisce -1
  • s.rfind(s1): come sopra, ma inizia dalla fine di s (la r sta per reversed)
  • s.replace(old,new): sostituisce tutte le sottostringhe uguali a old in s con la stringa new
  • s.strip(): rimuove tutti i caratteri blanks dalla stringa s
  • s.split(d): suddivide la stringa in sottostringhe usando il carattere d come separatore

Dizionari

I dizionari sono una struttura dati molto utilizzati in python. Sono delle strutture dati che corrispondono a delle liste di coppie (key, value). I dizionari sono degli oggetti mutabili come le liste, ma non sono una sequenza ordinata, e quindi non possono essere indicizzati con dei numeri interi.

La key viene utilizzata come chiave per indicizzare un value. Per esempio:

Dict["hello"] = "ciao"

Abbiamo la chiave "hello" utilizzata per indicizzare l'elemento "ciao" nel dizionario Dict. Per semplicita', potete pensare ai dizionari a come dei vettori di elementi indicizzati da altri oggetti immutabili, come ad esempio delle stringhe.

ESEMPIO:


In [ ]:
# Creo un dizionario vuoto
Vocabolario = dict()
print(type(Vocabolario))

In [ ]:
Vocabolario["keep"] = "stai"
Vocabolario["calm"] = "sereno"
print("Enrico,", Vocabolario["keep"], Vocabolario["calm"])

In [ ]:
# E` possibile enumerare i dizionari con un ciclo for
Vocabolario["hello"] = "ciao"
for key in Vocabolario:
    print("chiave:", key, "- valore:", Vocabolario[key])

Metodi utili sui dizionari

I seguenti metodi sono molto utili per usare i dizionari:

  • len(d): restituisce il numero di elementi nel dizionario d
  • d.keys(): restituisce una vista delle chiavi del dizionario d
  • d.values(): restituisce una vista dei valori del dizionario d
  • key in d: restituisce True se la chiave key è nel dizionario d
  • d.get(key, value): restituisce d[key] se key è in d, altrimenti restituisce il valore value
  • d[key] = value: associa il valore value alla chiave key nel dizionario d
  • del d[key]: rimuove la chiave k dal dizionario d
  • for key in d: itera sulle chiavi del dizionario d

Insiemi

È possibile definire degli insiemi di elementi usando oggetti di tipo set: questi sono delle collezioni non ordinate di oggetti mutabili (più precisamente su cui è definita una funzione di hash). Sono molto utili quando si deve controllare l'appartenenza di un elemento ad un dato insieme, avere delle collezioni di oggetti senza duplicati, oppure se si devono effetturare delle operazioni su insiemi come unione, intersezione e differenza tra insiemi.

Per maggiori dettagli, si rimanda alla documentazione ufficiale.

ESEMPIO:


In [ ]:
A = set([2,3,4,2,4,6,7,8])
print(A)
print(3 in A)
B = set([4,4,2,1])
A = A.intersection(B)
print(A)
A = A.difference(B)
print(A)

Classi e Oggetti

Il piu' semplice esempio di built-in oggetto in python e' il tipo complex:

x = 1+3j

In questo caso una semplice implementazione in python potrebbe essere:


In [ ]:
class MyComplex:
    
    def __init__(self, Real, Img):
        self.real = Real
        self.img = Img
        
    def __str__(self):
        s = str(self.real)
        if self.img >= 0:
            s += '+'
        s += str(self.img)+'j'
        return s
    
    def coniugate(self):
        return MyComplex(self.real, -self.img)

In [ ]:
c = MyComplex(1,4)

In [ ]:
print(c)

In [ ]:
print(c.coniugate())

In [ ]:
c.img

In [ ]:
type(c)

In [ ]:
c.img = 5
print(c.coniugate())

Lettura di file di testo

Per leggere un file di testo chiamato filename, bisogna prima "aprirlo" con il comando open(filename, mode) usando la modalità mode='r'. Si usi invece mode='w' se si vuole scrivere un nuovo file oppure mode='a' se vuole aggiungere righe in coda ad un file esistente. Il metodo open restituisce un file handle (una sorta di puntatore) per poter accedere al file e manipolarne il contenuto.

ESEMPIO: Il seguente codice, apre il file di testo train-small.csv (il suffisso ".csv" sta per "comma separated values") e ne legge una riga alla volta stampandola a video. Il file contiene le informazioni di 10 passeggeri del Titanic.


In [ ]:
doc = open('data/train-small.csv', 'r')
for row in doc:
    print(row)
doc.close()

Si noti che in effetti ogni riga del file contiene una serie di valori separati da una virgola e che è come se ogni riga contennesse già il carattere \n che stampa delle righe vuote.

Metodi utili per manipolare files

I seguenti metodi sono molto utili per manipolare dei files:

  • open(filename, mode): apre il file chiamato "filename" in modalità mode (lettura, scrittura, aggiunta)
  • pf.read(): restituisce un'unica stringa contenente l'intero contenuto del file
  • pf.readline(): restituisce la prossima riga associata al file handler pf
  • pf.readlines(): restituisce una lista di stringhe, una per ciascuna riga contenuta nel file
  • pf.write(s): scrive la stringa s nel file pf
  • pf.writeLines(S): scrive ciascun elemento della collezione di stringhe contenute in S in una riga diversa del file pf
  • pf.close(): chiude il file handler pf

ESERCIZIO 3: Si scriva una funzione che prenda in input un nome di un file, apra il file in lettura, e su ogni riga:

  1. rimuove i caratteri \n. Suggerimento: usare la funzione replace()
  2. rimuove le doppie virgolette "
  3. divide la riga in sottostringhe. Suggerimento: usare la funzione split()

Infine la funzione deve restituire il numero di persone che nell'elenco hanno il secondo campo pari a 1. Si testi la funzione usando il file train.csv che si trova nella cartella data.


In [ ]:
def ReadAndCount(filename):
    # DA COMPLETARE
    return 0

print(ReadAndCount('data/train.csv'))

ESERCIZIO 4: Si scriva una funzione simile alla precedente, chiamata ReadAndPlotAge, ma che restituisca una lista contenente l'èta di ciascun passegero (il sesto campo di ciascuna riga). Si usi tale lista per fare due plot (si vedano i notebook della seconda lezione e relativi esempi di uso di matplotlib con la funzione hist):

  1. Si rappresenti l'istogramma dell'età dei passegeri usando 8 bin
  2. Si rappresenti la distribuzione cumulatà dell'età dei passengeri, usando 100 bins

Si testi la funzione usando il train.csv.


In [ ]:
def ReadAndPlotAge(filename):
    # DA COMPLETARE
    # DA COMPLETARE
    # DA COMPLETARE
    pass

ESERCIZIO 5: Si scriva simile alla precedente, chiamata ReadAndPlotFare, ma che usi il prezzo del biglietto invece dell'età dei passeggeri.


In [ ]:
def ReadAndPlotFare(filename):
    # DA COMPLETARE
    # DA COMPLETARE
    # DA COMPLETARE
    pass

ESERCIZIO 6: Si scriva una funzione, chiamata ReadAndPlot, che generalizzi le due precedenti.


In [ ]:
def ReadAndPlot(filename):
    # DA COMPLETARE
    # DA COMPLETARE
    # DA COMPLETARE
    pass