Chapter 12 TupleQuesto capitolo illustra un altro tipo di dati predefinito, le tuple, per poi mostrare come liste, tuple e dizionari possono lavorare insieme. Viene inoltre presentata una utile caratteristica per le liste di argomenti a lunghezza variabile: gli operatori di raccolta e spacchettamento. 12.1 Le tuple sono immutabiliUna tupla è una sequenza di valori. I valori possono essere di qualsiasi tipo, sono indicizzati tramite numeri interi, e in questo somigliano moltissimo alle liste. La differenza fondamentale è che le tuple sono immutabili. Sintatticamente, la tupla è un elenco di valori separati da virgole: >>> t = 'a', 'b', 'c', 'd', 'e' Sebbene non sia necessario, è convenzione racchiudere le tuple tra parentesi tonde: >>> t = ('a', 'b', 'c', 'd', 'e') Per creare una tupla con un singolo elemento dobbiamo aggiungere la virgola finale dopo l’elemento: >>> t1 = 'a', >>> type(t1) <class 'tuple'> Senza la virgola, infatti, un unico valore tra parentesi non è una tupla ma una stringa: >>> t2 = ('a') >>> type(t2) <class 'str'> Un altro modo di creare una tupla è usare la funzione predefinita tuple. Se priva di argomento, crea una tupla vuota: >>> t = tuple() >>> t () Se l’argomento è una sequenza (stringa, lista o tupla), il risultato è una tupla con gli elementi della sequenza: >>> t = tuple('lupini') >>> t ('l', 'u', 'p', 'i', 'n', 'i') Siccome tuple è il nome di una funzione predefinita, bisogna evitare di usarlo come nome di variabile. La maggior parte degli operatori delle liste funzionano anche con le tuple. L’operatore parentesi quadre indicizza un elemento della tupla: >>> t = ('a', 'b', 'c', 'd', 'e') >>> t[0] 'a' E l’operatore di slicing seleziona una serie di elementi consecutivi: >>> t[1:3] ('b', 'c') Ma a differenza delle liste, se cercate di modificare gli elementi di una tupla ottenete un messaggio d’errore: >>> t[0] = 'A' TypeError: object doesn't support item assignment Dato che le tuple sono immutabili, non si può modificarne gli elementi. Ma potete sostituire una tupla con un’altra: >>> t = ('A',) + t[1:] >>> t ('A', 'b', 'c', 'd', 'e') Questa istruzione crea una nuova tupla e poi fa in modo che t si riferisca ad essa. Gli operatori di confronto funzionano con le tuple e le altre sequenze; Python inizia a confrontare il primo elemento di ciascuna sequenza. Se sono uguali, passa all’elemento successivo e così via, finché non trova due elementi diversi. Gli eventuali elementi che seguono vengono trascurati (anche se sono molto grandi). >>> (0, 1, 2) < (0, 3, 4) True >>> (0, 1, 2000000) < (0, 3, 4) True 12.2 Assegnazione di tuplaSpesso è utile scambiare i valori di due variabili tra loro. Con le istruzioni di assegnazione convenzionali, dobbiamo usare una variabile temporanea. Per esempio per scambiare a e b: >>> temp = a >>> a = b >>> b = temp Questo metodo è farraginoso; l’utilizzo dell’assegnazione di tupla è più elegante: >>> a, b = b, a La parte sinistra dell’assegnazione è una tupla di variabili; la parte destra una tupla di valori o espressioni. Ogni valore è assegnato alla rispettiva variabile. Tutte le espressioni sulla destra vengono valutate prima della rispettiva assegnazione. Ovviamente il numero di variabili sulla sinistra deve corrispondere al numero di valori sulla destra: >>> a, b = 1, 2, 3 ValueError: too many values to unpack Più in generale, il lato destro può essere composto da ogni tipo di sequenza (stringhe, liste o tuple). Per esempio, per separare un indirizzo email tra nome utente e dominio, potete scrivere: >>> indirizzo = 'monty@python.org' >>> nome, dominio = indirizzo.split('@') Il valore di ritorno del metodo split è una lista con due elementi; il primo è assegnato alla variabile nome, il secondo a dominio. >>> nome 'monty' >>> dominio 'python.org' 12.3 Tuple come valori di ritornoIn senso stretto, una funzione può restituire un solo valore di ritorno, ma se il valore è una tupla, l’effetto pratico è quello di restituire valori molteplici. Per esempio, se volete dividere due interi e calcolare quoziente e resto, è poco efficiente calcolare x/y e poi x%y. Meglio calcolarli entrambi in una volta sola. La funzione predefinita divmod riceve due argomenti e restituisce una tupla di due valori, il quoziente e il resto. E potete memorizzare il risultato con una tupla: >>> t = divmod(7, 3) >>> t (2, 1) Oppure, usate l’assegnazione di tupla per conservare gli elementi separatamente: >>> quoziente, resto = divmod(7, 3) >>> quoziente 2 >>> resto 1 Ecco un esempio di funzione che restituisce una tupla: def min_max(t): return min(t), max(t) max e min sono funzioni predefinite che estraggono da una sequenza il valore massimo e quello minimo. 12.4 Tuple di argomenti a lunghezza variabileLe funzioni possono ricevere un numero variabile di argomenti. Un nome di parametro che comincia con *, raccoglie gli argomenti in una tupla. Per esempio, stampatutti riceve un qualsiasi numero di argomenti e li visualizza: def stampatutti(*args): print(args) Il parametro di raccolta può avere qualunque nome, ma per convenzione si usa args. Ecco come funziona: >>> stampatutti(1, 2.0, '3') (1, 2.0, '3') Il contrario della raccolta è lo spacchettamento. Se avete una sequenza di valori e volete passarla a una funzione come argomenti multipli, usate ancora l’operatore *. Per esempio, divmod richiede esattamente due argomenti; passare una tupla non funziona: >>> t = (7, 3) >>> divmod(t) TypeError: divmod expected 2 arguments, got 1 Ma se spacchettate la tupla, funziona: >>> divmod(*t) (2, 1) Molte funzioni predefinite possono usare le tuple di argomenti a lunghezza variabile. Ad esempio, max e min ricevono un numero qualunque di argomenti: >>> max(1,2,3) 3 >>> sum(1,2,3) TypeError: sum expected at most 2 arguments, got 3 Per esercizio, scrivete una funzione di nome sommatutto che riceva un numero di argomenti a piacere e ne restituisca la somma. 12.5 Liste e tuplezip è una funzione predefinita che riceve due o più sequenze e restituisce una lista di tuple, dove ciascuna tupla contiene un elemento di ciascuna sequenza. Il nome si riferisce alla cerniera-lampo (zipper), che unisce due file di dentelli, alternandoli. Questo esempio abbina una stringa e una lista: >>> s = 'abc' >>> t = [0, 1, 2] >>> zip(s, t) <zip object at 0x7f7d0a9e7c48> Il risultato è un oggetto zip capace di iterare attraverso le coppie. L’uso più frequente di zip è in un ciclo for: >>> for coppia in zip(s, t): ... print(coppia) ... ('a', 0) ('b', 1) ('c', 2) Un oggetto zip è un tipo di iteratore, che è un qualsiasi oggetto in grado di iterare attraverso una sequenza. Gli iteratori sono per certi versi simili alle liste, ma a differenza di queste ultime, non si può usare un indice per scegliere un elemento da un iteratore. Se desiderate usare operatori e metodi delle liste, potete crearne una utilizzando un oggetto zip: >>> list(zip(s, t)) [('a', 0), ('b', 1), ('c', 2)] Il risultato è una lista di tuple, e in questo esempio ciascuna tupla contiene un carattere della stringa e il corrispondente elemento della lista. Se le sequenze non sono della stessa lunghezza, il risultato ha la lunghezza di quella più corta: >>> list(zip('Anna', 'Edo')) [('A', 'E'), ('n', 'd'), ('n', 'o')] Potete usare l’assegnazione di tupla in un ciclo for per attraversare una lista di tuple: t = [('a', 0), ('b', 1), ('c', 2)] for lettera, numero in t: print(numero, lettera) Ad ogni ciclo, Python seleziona la tupla successiva all’interno della lista e ne assegna gli elementi a lettera e numero, quindi li stampa. Il risultato di questo ciclo è: 0 a 1 b 2 c Se combinate zip, for e assegnazione di tupla, ottenete un utile costrutto per attraversare due o più sequenze contemporaneamente. Per esempio, def corrispondenza(t1, t2): for x, y in zip(t1, t2): if x == y: return True return False Se volete attraversare gli elementi di una sequenza e i loro indici, potete usare la funzione predefinita enumerate: for indice, elemento in enumerate('abc'): print(indice, elemento) Il risultato di enumerate è un oggetto enumerate, che itera una sequenza di coppie; ogni coppia contiene un indice (a partire da 0) e un elemento della sequenza data. In questo esempio l’output è di nuovo: 0 a 1 b 2 c 12.6 Dizionari e tupleI dizionari supportano un metodo di nome items che restituisce una sequenza di tuple, dove ogni tupla è una delle coppie chiave-valore. >>> d = {'a':0, 'b':1, 'c':2} >>> t = d.items() >>> t dict_items([('c', 2), ('a', 0), ('b', 1)]) Il risultato è un oggetto >>> for chiave, valore in d.items(): ... print(chiave, valore) ... c 2 a 0 b 1 Come di consueto per i dizionari, gli elementi non sono in un ordine particolare. Per altro verso, potete usare una lista di tuple per inizializzare un nuovo dizionario: >>> t = [('a', 0), ('c', 2), ('b', 1)] >>> d = dict(t) >>> d {'a': 0, 'c': 2, 'b': 1} La combinazione di dict e zip produce un modo conciso di creare un dizionario: >>> d = dict(zip('abc', range(3))) >>> d {'a': 0, 'c': 2, 'b': 1} Anche il metodo dei dizionari update prende una lista di tuple e le aggiunge, come coppie chiave-valore, a un dizionario esistente. L’uso delle tuple come chiavi di un dizionario è frequente (soprattutto perché le liste non si possono usare in quanto mutabili). Per esempio, un elenco telefonico può mappare da coppie di nomi e cognomi nei numeri di telefono. Supponendo di aver definito cognome, nome e numero, possiamo scrivere: elenco[cognome,nome] = numero L’espressione tra parentesi quadre è una tupla. Possiamo usare l’assegnazione di tupla per attraversare questo dizionario. for cognome, nome in elenco: print(nome, cognome, elenco[cognome,nome]) Questo ciclo attraversa le chiavi in elenco, che sono tuple. Assegna gli elementi di ogni tupla a cognome e nome, quindi stampa il nome completo e il numero di telefono corrispondente. Ci sono due modi per rappresentare le tuple in un diagramma di stato. La versione più dettagliata mostra gli indici e gli elementi così come compaiono in una lista. Per esempio la tupla Ma in un diagramma più ampio è meglio tralasciare i dettagli. Per esempio, quello dell’elenco telefonico può essere come in Figura 12.2. Qui le tuple sono mostrate usando la sintassi di Python come abbreviazione grafica. Il numero di telefono nel diagramma è quello dei reclami della BBC, per cui vi prego, non chiamatelo. 12.7 Sequenze di sequenzeCi siamo concentrati finora sulle liste di tuple, ma quasi tutti gli esempi di questo capitolo funzionano anche con liste di liste, tuple di tuple, e tuple di liste. Per evitare di elencare tutte le possibili combinazioni, è più semplice usare il termine sequenze di sequenze. In molti casi, i diversi tipi di sequenze (strighe, liste, tuple) possono essere intercambiabili. E allora, con che criterio usarne una piuttosto di un’altra? Le stringhe sono ovviamente le più limitate, perché gli elementi devono essere dei caratteri. E sono anche immutabili. Se dovete cambiare i caratteri in una stringa, anziché crearne una nuova, utilizzare una lista di caratteri può essere una scelta migliore. Le liste sono usate più di frequente delle tuple, soprattutto perché sono mutabili. Ma ci sono alcuni casi in cui le tuple sono preferibili:
Siccome le tuple sono immutabili, non possiedono metodi come sort e reverse, che modificano delle liste esistenti. Però Python contiene la funzione sorted, che richiede una sequenza e restituisce una nuova lista con gli stessi elementi della sequenza, ordinati, e reversed, che prende una sequenza e restituisce un iteratore che attraversa la lista in ordine inverso. 12.8 DebugListe, dizionari e tuple sono esempi di strutture di dati; in questo capitolo abbiamo iniziato a vedere strutture di dati composte, come liste di tuple, o dizionari che contengono tuple come chiavi e liste come valori. Si tratta di elementi utili, ma soggetti a quelli che io chiamo errori di formato; cioè errori causati dal fatto che una struttura di dati è di tipo, dimensione o struttura sbagliati. Ad esempio, se un programma si aspetta una lista che contiene un numero intero e invece gli passate un intero puro e semplice (non incluso in una lista), non funzionerà. Per facilitare il debug di questo genere di errori, ho scritto un modulo di nome structshape che contiene una funzione, anch’essa di nome structshape, che riceve come argomento una qualunque struttura di dati e restituisce una stringa che ne riassume il formato. Potete scaricarlo dal sito http://thinkpython2.com/code/structshape.py Questo è il risultato per una lista semplice: >>> from structshape import structshape >>> t = [1,2,3] >>> structshape(t) 'list of 3 int' Un programma più aggraziato avrebbe scritto “list of 3 ints”, ma è più semplice non avere a che fare con i plurali. Ecco una lista di liste: >>> t2 = [[1,2], [3,4], [5,6]] >>> structshape(t2) 'list of 3 list of 2 int' Se gli elementi della lista non sono dello stesso tipo, structshape li raggruppa, in ordine, per tipo: >>> t3 = [1, 2, 3, 4.0, '5', '6', [7], [8], 9] >>> structshape(t3) 'list of (3 int, float, 2 str, 2 list of int, int)' Ecco una lista di tuple: >>> s = 'abc' >>> lt = zip(list(t, s)) >>> structshape(lt) 'list of 3 tuple of (int, str)' Ed ecco un dizionario di 3 elementi in cui corrispondono interi a stringhe >>> d = dict(lt) >>> structshape(d) 'dict of 3 int->str' Se fate fatica a tenere sotto controllo le vostre strutture di dati, structshape può esservi di aiuto. 12.9 Glossario
12.10 EserciziEsercizio 1 Scrivete una funzione di nome
Esercizio 2
Ancora anagrammi!
Esercizio 3
Si ha una metatesi quando una parola si può ottenere scambiando due lettere di un’altra parola, per esempio, “conversa” e “conserva”. Scrivete un programma che trovi tutte le coppie con metatesi nel dizionario. Suggerimento: non provate tutte le possibili coppie di parole e non provate tutti i possibili scambi. Soluzione: http://thinkpython2.com/code/metathesis.py. Fonte: Esercizio suggerito da un esempio nel sito http://puzzlers.org.
Esercizio 4
Ed ecco un altro quesito di Car Talk: (http://www.cartalk.com/content/puzzlers): Qual è la più lunga parola inglese che rimane una parola valida se le togliete una lettera alla volta? Le lettere possono essere rimosse sia agli estremi o in mezzo, ma senza spostare le lettere rimanenti. Ogni volta che togliete una lettera, ottenete un’altra parola inglese. Se andate avanti, ottenete un’altra parola. Ora, voglio sapere qual è la parola più lunga possibile e quante lettere ha. Scrivete un programma che trovi tutte le parole che sono riducibili in questa maniera, quindi trovate la più lunga. Questo esercizio è un po’ più impegnativo degli altri, quindi eccovi alcuni suggerimenti:
Soluzione: http://thinkpython2.com/code/reducible.py. |
ContributeIf you would like to make a contribution to support my books, you can use the button below. Thank you!
Are you using one of our books in a class?We'd like to know about it. Please consider filling out this short survey.
|