Il 0% ha trovato utile questo documento (0 voti)
119 visualizzazioni106 pagine

Python Nuovo Bonus

Il libro di Tony Chan è una guida completa alla programmazione ad oggetti in Python, progettata per principianti e include oltre 200 esercizi risolti. Viene esplorato l'uso di Python in vari contesti, come lo sviluppo di intelligenza artificiale e la creazione di siti web, evidenziando la sua versatilità e facilità d'uso. La guida tratta anche la storia di Python, il suo sviluppo e la crescente popolarità nel campo della scienza dei dati.

Caricato da

giacomo159753
Copyright
© © All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
119 visualizzazioni106 pagine

Python Nuovo Bonus

Il libro di Tony Chan è una guida completa alla programmazione ad oggetti in Python, progettata per principianti e include oltre 200 esercizi risolti. Viene esplorato l'uso di Python in vari contesti, come lo sviluppo di intelligenza artificiale e la creazione di siti web, evidenziando la sua versatilità e facilità d'uso. La guida tratta anche la storia di Python, il suo sviluppo e la crescente popolarità nel campo della scienza dei dati.

Caricato da

giacomo159753
Copyright
© © All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 106

PYTHON

# Guida completa alla programmazione ad


oggetti per principianti
Diventa un programmatore sviluppando un'IA con Machine Learning e un
sito web in 10 step. Contiene oltre 200 esercizi risolti

Di Tony Chan

1
© Copyright 2023 - Tutti i diritti riservati.

I contenuti di questo libro non possono essere riprodotti, duplicati o trasmessi


senza il diretto consenso scritto dell'autore o dell'editore.
In nessuna circostanza qualsiasi colpa o responsabilità legale sarà attribuita
all'editore, o all'autore, per eventuali danni, riparazioni o perdite monetarie a
causa delle informazioni contenute in questo libro.

Avviso legale:

Questo libro è protetto da copyright ed è destinato esclusivamente all'uso


personale. Non è possibile modificare, distribuire, vendere, utilizzare, citare o
parafrasare il contenuto senza il consenso diretto dell'autore o dell'editore.

Dichiarazione di non responsabilità:

Si prega di notare che il contenuto di questo libro è destinato esclusivamente a fini


educativi e di intrattenimento. Ogni sforzo è stato fatto per presentare
un'informazione accurata, attuale, affidabile e completa. Nessuna garanzia di alcun
tipo è dichiarata o implicita. I lettori riconoscono che l'autore non è impegnato
nella fornitura di consulenza legale, finanziaria, medica o professionale. Il
contenuto del libro deriva in parte da diverse fonti. Consultare un professionista
autorizzato prima di tentare qualsiasi tecnica descritta.
Leggendo questo testo, il lettore accetta che in nessun caso l'autore sarà ritenuto
responsabile per eventuali perdite, dirette o indirette, subite a seguito dell'uso
delle informazioni contenute in questo documento, incluse omissioni o inesattezze.

_______________________

2
# L’autore

Tony Chan è nato a South Gate, in


California, Stati Uniti, da genitori
immigrati cinesi. A 11 anni scrive il
suo primo programma software in
Basic, a 17 insegna agli amici
programmazione e a 20 si laurea in
informatica.
Recentemente ha partecipato come consulente alla redazione di alcuni testi
su intelligenza artificiale e linguaggio macchina. Successivamente decide di
pubblicare i suoi libri sulla programmazione sia negli Stati Uniti, dove vive e
lavora, ma anche in Italia, che ama e visita spesso. Un giorno, un pizzaiolo
nelle Marche gli ha dedicato il “Calzone con incapsulamento”, per rendere
omaggio a uno dei principi dell’OOP. Il ripieno poteva essere mangiato solo
con getForchetta e getColtello.
Di Tony si dice che dorme sempre assieme o vicino a un pc, afferma che il
calore emesso dalla CPU che lavora gli concilia il sonno...

3
Indice dei contenuti

# Introduzione 8

# 1 - Le prime basi 30

Le costanti e le variabili 30
Tipologia di variabili 32
Operatori e metodi 32
I dati 34
Le raccolte di dati 40
Il set 40
Le liste 44
I dizionari 47
Le tuple 49
Le iterazioni 52
Distinzione tra iterabile e iteratore 58
Approfondiamo gli operatori 59
I primi passi 62
Typecasting 63
Utilizzare i moduli 67
Quiz & esercizi / Riassunto 70
# 2 - L’espressione in Python 74
I nomi delle variabili 77
Specificare il tipo 79
Gli identificatori 81
Letterali ed espressioni 82
La gestione delle eccezioni 86
La gestione dei file e directory 91
La gestione del tempo 98
PYTHON AVANZATO – Protocolli 100
4
PYTHON AVANZATO – Metodi aggiuntivi sui tipi di dati 103

Quiz & esercizi / Riassunto 104


# 3 - Le funzioni 108
Gli argomenti della funzione 110
Definizione di una funzione 111
Ambito delle variabili nelle funzioni, namespace 115
Le funzioni nel dettaglio 118
Funzioni come oggetti o nidificate 121
Le funzioni lambda 122
I decoratori 131
I callback 134
I generatori 135
Programmazione asincrona 139
PYTHON AVANZATO – Concorrenza e parallelismo 149
Quiz & esercizi / Riassunto 154
# 4 - Le classi 158
Creazione di oggetti 161
Il costruttore e la definizione della classe 162
Il decoratore nelle classi 167
Ereditarietà 177
Classi nidificate 181
Metodi magici 182
Variabili di classe, metodi di classe e metodi statici 189
Quiz & esercizi / Riassunto 193
# 5 - I cardini dell’OOP 197
Incapsulamento 198
Accesso diretto alle variabili 201
Problemi con la convalida dei dati 203
Getter e Setter 206
Polimorfismo 210

5
Ereditarietà 215

Implementazione dell’ereditarietà 218


Classi e metodi astratti 226
Ereditarietà multipla 232
Quiz & esercizi / Riassunto 233
# 6 - Le espressioni regolari 240
Qualificatori, caratteri e operatori nelle regexp 242
Le sequenze speciali o complesse 249
Il modulo RE 259
Usare gli oggetti compilati 263
Quiz & esercizi 265
# 7 - I mille volti di Python 269
Creare un sito web con Django 270
Analisi dei dati con Matplotlib 278
# 8 - IA e Machine Learning 285
Matrici, tensori e array multidimensionali 298
Le librerie Numpy e Scikit-Learn 299
Quiz & esercizi 304
# 9 - Soluzioni esercizi 308
Paragrafo 1 308
Paragrafo 2 309
Paragrafo 3 311
Paragrafo 4 314
Paragrafo 5 316
Paragrafo 6 321
Paragrafo 8 323
Link download gratuito EBOOK - Bibliografia 327

6
7
# Introduzione

In questo testo imparerai uno dei linguaggi di programmazione più importanti e


diffusi al mondo. In precedenza, ho discusso del suo contendente in "JavaScript, la
guida definitiva". Tuttavia, è importante sottolineare che non esiste un linguaggio
migliore in assoluto e la scelta dipende dalle specifiche esigenze del progetto. In
generale, Python, JavaScript e pochi altri sono considerati le pietre angolari per
imparare al meglio la programmazione. In particolare, Python è visto come il
numero uno da molti esperti del settore, e nel corso del libro vedremo anche il
motivo di questa reputazione.
Parliamo ora di Python in dettaglio. Innanzitutto, è importante sapere che è un
linguaggio di programmazione generico, ovvero non è stato progettato per
affrontare una specifica problematica, ma è estremamente versatile e può essere
usato in diversi contesti. Infatti, è impiegato in una vasta gamma di settori, tra cui
la creazione di applicazioni web, la gestione dei sistemi, l'analisi di grandi quantità
di dati e lo sviluppo d’intelligenze artificiali.
Python eccelle particolarmente nella scrittura di script di sistema, nell'elaborazione
di Big Data, nell'esecuzione di calcoli matematici e nella connessione a sistemi di
database. Inoltre, è molto utile per la lettura e la modifica di file, la creazione di
applicazioni web su un server, la prototipazione rapida e la creazione di flussi di
lavoro. Grazie alle sue capacità, è adatto alla produzione di software, allo sviluppo
backend web e alle applicazioni su dispositivi mobili.
Tuttavia, il caso d'uso più tipico per Python è come linguaggio di scripting o per
l’automazione. Durante la creazione di script, Python non è considerato un
sostituto degli script di Shell o dei file batch, ma viene utilizzato per automatizzare
le interazioni con i browser web o le interfacce utente grafiche (GUI) delle
8
applicazioni. Pertanto, con Python, possiamo creare applicazioni GUI sia a riga di
comando che multipiattaforma e distribuirle come contenuti eseguibili.
L'analisi sofisticata dei dati è diventata una delle aree più fiorenti dell'IT e la
maggior parte delle librerie di software e data science utilizzate per
l'apprendimento automatico hanno interfacce basate su Python, rendendo così
questa lingua la più popolare interfaccia di comando di alto livello. I framework
web di terze parti forniscono modi rapidi e convenienti per creare qualsiasi cosa,
da semplici righe di codice a siti completamente funzionanti con database annessi.
Nonostante Python non sia noto per le sue performance di alto livello nella
gestione di grandi volumi di traffico web, recentemente alcune librerie hanno
migliorato molto le prestazioni in questo contesto. Python funziona anche come
generatore di codice altamente efficiente, rendendo possibile la scrittura di
applicazioni che manipolano le proprie funzioni che sarebbero difficili da
implementare in altri linguaggi. Questa codifica è spesso descritta come un
"linguaggio collante", nel senso che può rendere nuovamente interoperabili codici
disparati. Pertanto, se si dispone di applicazioni o domini di programma che si
desidera collegare tra loro, è possibile utilizzare Python per questa attività.
Oltre a questo, bisogna assolutamente segnalare che il linguaggio è di alto livello,
cioè, in poche parole, quanto più vicino al codice macchina è un linguaggio di
programmazione. Le lingue di basso livello si riferiscono proprio a questo caso,
mentre quelle di livello alto, al contrario, tendono ad avvicinarsi al nostro
linguaggio, quello che parliamo tutti i giorni. C# e Python sono considerati
linguaggi di alto livello, mentre C++ rappresenta quello che è noto come un
linguaggio di livello medio poiché offre alcune funzionalità piuttosto solide per i
programmatori più avanzati. Questa distinzione serve a farci capire gli sforzi che
sono stati fatti per creare dei linguaggi di alto livello per permettere a tutti di
avvicinarsi al mondo della programmazione, ma anche per fare in modo che gli
sviluppatori possano concentrarsi più sulla propria idea evitando perdite di tempo
per assegnare risorse alla ram o alla cpu, per esempio, perché bisognava fare

9
questo, e molto altro, nei linguaggi di basso livello, come l’Assembly.
Infine apprenderemo che Python è un linguaggio orientato agli oggetti (OOP). In
pratica, riassumendo all’osso, la programmazione OOP si contrappone a quella
procedurale. I due stili, o, più tecnicamente paradigmi, si differenziano dal metodo
con cui si arriva alla risoluzione del medesimo problema. Senza approfondire,
potremmo condensare le differenze nel fatto che la programmazione procedurale
segue una logica più rigida con un listato che è un susseguirsi di procedure che
comunicano tra di loro in cui la sequenza del codice ha una sua rilevanza. Mentre,
nella programmazione OOP i dati del listato sono utilizzati come oggetti con
determinate proprietà e metodi, per poi relazionarsi tra di loro. In questo caso la
sequenza del codice non ha importanza. Inoltre, esistono molte funzionalità
aggiuntive rispetto alle loro controparti procedurali, ad esempio l'incapsulamento,
una tecnica utilizzata per isolare parti del codice dall'una e dall'altra
implementando identificatori di accesso. Esamineremo l'incapsulamento e altre
specifiche relative agli oggetti in modo più dettagliato in seguito. Da segnalare
infine che questi due stili possono coesistere, soprattutto nei linguaggi OOP,
quando scriveremo un listato, avremo sicuramente porzioni del nostro codice
prettamente procedurali.

Breve storia
Python è stato progettato da Guido Van Rossum e prende il suo nome da un
popolare programma comico inglese trasmesso in TV, chiamato Monty Python.
Il lavoro ufficiale su Python è iniziato nel dicembre 1989, praticamente come un
hobby. Infatti Van Rossum era alla ricerca di un lavoro interessante che lo tenesse
occupato a Natale. Ha quindi preso la sintassi del linguaggio ABC del 1975 con
alcune delle sue migliori caratteristiche, correggendo la maggior parte dei
problemi. Nacque così, in breve tempo, un nuovo linguaggio di scripting.
Python è stato ufficialmente rilasciato nel 1991. Inizialmente era un po’ grezzo,
tuttavia, la sua carta vincente era quella di fornire una migliore leggibilità del

10
codice e aumentare la produttività complessiva dello sviluppatore.
Dopo il lancio, per molti anni non andò bene. Le soluzioni affidabili, come ASP.NET,
Java e PHP, la fecero da padrone per diverso tempo. Poi, intorno agli anni 2000,
alcune nuove startup iniziarono a utilizzare Python per i loro progetti. Molte altre
con budget ridotti le seguirono. Come mai? Per la sua facilità d'uso, il rapido
sviluppo e certamente a causa del basso costo di gestione.
Python raggiunse il culmine della sua popolarità quando Dropbox fu introdotto sul
mercato. La leggenda narra che spesso, lo studente del MIT Drew Houston
dimenticava la chiavetta USB con i suoi dati, creandogli non pochi problemi.
Questa fu una delle ragioni per cui iniziò a pensare a una soluzione migliore per la
condivisione dei file, creando così Dropbox, scritto tutto in Python.
L’impresa raggiunse i tre milioni di utenti alla fine del 2009, e grazie alla sua
rapida ascesa, molte aziende iniziarono a considerare Python per i loro business.
La fine degli anni 2000 ha poi visto l’esplosione dei social media. E man mano che
la loro influenza cresceva, aumentava anche la quantità di dati che dovevano
gestire. Allo stesso tempo, Python divenne rapidamente il linguaggio preferito per
accedere a quei dati, specialmente nel mondo tecnologico e delle startup. Durante
la crisi finanziaria del 2008, l'automazione conquistò il mondo finanziario. Le
istituzioni finanziarie avevano la necessità di elaborare i Big Data e iniziarono a
esaminare i dati per individuare modelli, prendere decisioni e gestire i processi
economici. Infatti le aziende avevano molti dati, ma nessun modo semplice e
chiaro per accedervi. Fu allora che iniziarono a eseguire analisi quantitative sui dati
finanziari utilizzando Python. Altri importanti pacchetti come NumPy, Scikit e
Matplotlib contribuirono al successo e presto Python divenne la scelta migliore per
la scienza dei dati.
Inoltre, con la rapidissima espansione del web, esplosero anche i cosiddetti boot
camp (corsi intensivi sulla programmazione). Python è sempre stato aperto a tutti,
e di conseguenza, il primo linguaggio insegnato in molti di questi programmi dei
boot camp è stato proprio Python. Seguito poi da scuole superiori e università in

11
tutto il mondo. La strada era tracciata, e anche se emergeva qualche problema,
come ad esempio la sua lentezza, tuttavia i punti a favore erano molti di più.
Infatti, sebbene l'output di Python sia spesso più flemmatico del software
realizzato con C#, è comunque diventato piuttosto popolare negli anni, perché
gratuito, multipiattaforma, facile da usare e apprendere, ricchissimo di librerie e
molto flessibile. Sostanzialmente può gestire qualsiasi cosa, dalla creazione di
semplici applicazioni per il web a software desktop più pesanti. Ha persino trovato
una nicchia nell'industria dei videogiochi ed è sempre più utilizzato nel machine
learning, cioè quel particolare settore relativo all’intelligenza artificiale in cui un
software è in grado d’imparare e migliorare le proprie performance in base ai dati
utilizzati.
Infine, al giorno d'oggi, chi vuole diventare uno sviluppatore full-stack, necessita
d’iniziare con le basi del software frontend e di backend. Molti programmi didattici
hanno stabilito che la transizione da JavaScript a Python sia agevole per gli
studenti alle prime armi, anche se i linguaggi sono differenti. Quindi, se un giorno
dovessimo diventare uno sviluppatore frontend che lavora con JavaScript e sta
avendo a che fare con un'interfaccia di programmazione dell'applicazione (API),
potremmo trovare piuttosto noioso creare un backend per queste applicazioni. Ma
avremo una migliore alternativa con Python. Infatti, i suoi framework come Flask e
Django offrono soluzioni semplici agli sviluppatori che desiderano creare software
di backend e anche potenti API con cui coordinarsi.

Lo standard e le versioni di Python


Le linee guida del linguaggio sono definite in The Python Language Reference
(https://docs.python.org/3/reference/). Esse prevedono e promuovono
l’implementazione di Python. Nelle proposte di miglioramento sono descritti diversi
standard, di solito sono indicati come PEP più un numero. Si può visionare l’elenco
completo e tutte le novità sul sito ufficiale: https://peps.python.org/.
Detto questo, è importante sapere che Python è disponibile in due diverse

12
versioni: la più vecchia, la seconda, dal 2020 non è più supportata. Mentre Python
tre, la versione attuale e futura del linguaggio, ha molte funzionalità aggiornate
che non erano incluse in quella precedente, come ad esempio un interprete
migliore. Qualche anno fa c’era molta concorrenza tra i due, ma da quando la
versione tre ha incluso alcune funzioni importanti, ha conquistato tutti. Questi
cambiamenti includevano la semplificazione e l’uniformazione per la scrittura delle
stringhe adottando lo standard Unicode come predefinito, l’implementazione
dell’istruzione print, il miglioramento e la semplificazione di alcune regole
riguardanti gli insiemi, e molto altro ancora.
All'inizio, l'approvazione dei programmatori nei confronti di Python tre è stata
rallentata per qualche tempo a causa della mancanza di sostegno per le librerie di
terze parti. Poiché la maggior parte di queste supportava la versione precedente,
la transazione ha richiesto molto tempo. Ma negli ultimi tre anni, il numero di
librerie compatibili con entrambe le versioni di Python è aumentato enormemente.
Oggi, dopo il parziale declino della seconda versione, l’ultima è senza dubbio la
migliore scelta possibile. Grazie alla sua semplicità, le migliori aziende tech, come
ad esempio Meta, Google, Mozilla, Reddit, Spotify e Cisco, hanno scelto Python per
vari scopi come lo sviluppo, lo scripting, la generazione e il test del software. È
stato anche fonte d’ispirazione per molti altri linguaggi di programmazione come
ad esempio Boo, CoffeeScript, Cobra, ECMAScript, Groovy, Ruby, e Swift Go.
Il successo di Python si basa anche sulle librerie, una standard molto solida e un
generoso assortimento creato da sviluppatori terze parti. La libreria standard
fornisce moduli per attività di programmazione comuni come matematica, gestione
delle stringhe, accesso a file e directory, networking, operazioni asincrone,
threading e gestione di più processi. Inoltre, include anche moduli che gestiscono
attività di programmazione comuni come la lettura e la scrittura di formati di file
strutturati, la manipolazione di file compressi e l'utilizzo di protocolli Internet. La
distribuzione predefinita fornisce anche un accesso integrato al database SQLite 3.
Ci sono migliaia di librerie di terze parti disponibili tramite l'indice dei pacchetti.

13
Per esempio le librerie Pillow e MoviePy forniscono strumenti per la gestione di
immagini e video. Framework come Flask e Django consentono un rapido sviluppo
dei servizi web, e anche possibile gestire più servizi cloud utilizzando Apache
Libcloud, solo per citarne alcune.

Python: pro e contro


I motivi per cui dovremo utilizzare questo fantastico linguaggio sono diversi, ne
elencheremo i più degni di nota, e poi segnaleremo anche i contro:
- Rinomato e diffuso: da tanti anni Python è considerato uno dei migliori
linguaggi di programmazione esistenti, se così non fosse, ovviamente sarebbe
sbagliato per tutti investire tempo e denaro nell'apprendimento di qualcosa che
non è abbastanza conosciuto, per una lunga serie di motivazioni, tutte opposte a
ciò che segue nel paragrafo.
Secondo una pletora di sondaggi presenti online (tra cui Gartner), svariati blog di
programmatori e anche la carta stampata (The Economist e Forbes), Python è
probabilmente il linguaggio più popolare, ricercato sui motori di ricerca e utilizzato
al mondo (https://pypl.github.io/PYPL.html). Altri, come Ruby o Fortran, hanno
visto un drastico declino, mentre C e C++, nonostante la complessità, sono rimasti
stabili. A suggello di quanto affermato finora, la tendenza futura inerente alla
celebrità di Python, è prevista dalla maggior parte degli analisti sostanzialmente
invariata, sempre al top.
- Facile da apprendere: nonostante sia un linguaggio potente e multifunzionale,
non ci vogliono certo anni o corsi di laurea per impararlo. La maggior parte dei
professionisti del settore ha acquisito le sue basi (che includono la sintassi, le
parole chiave e i tipi di dati di Python) attraverso uno di vari corsi presenti online
che, in media, richiedono solo 6-8 settimane. E, per chi ha una precedente
esperienza con i linguaggi di programmazione, potrebbe volerci ancora meno.
- Ha una vasta community: innanzitutto perché è un linguaggio di
programmazione open source. Ciò significa che chiunque può modificarlo o

14
alterarlo. Open source significa anche essere gratuiti e consentire la formazione di
librerie, framework e strumenti di estensione per aiutare a mantenere il linguaggio
adattabile e compatibile nel tempo. Sicuramente il successo di Python finora si è
realizzato perché c'è una comunità di utenti molto solidale e impegnata.
Inoltre, esiste una pagina dedicata collegata a diversi gruppi e forum della
community. Vari utenti, soprattutto programmatori, si incontrano periodicamente
per condividere idee e apprendere nuove tecniche. Forniscono inoltre un'ottima
assistenza ai principianti e offrono una piattaforma che consente agli utenti di
socializzare con altre persone con interessi simili. Gli incontri sono di solito una
volta al mese; sono molto informali e aperti a tutti. La community conta circa
novecentomila utenti. È possibile trovare un gruppo nella nostra area attraverso
questo link: https://wiki.python.org/moin/LocalUserGroups, in Italia il sito di
riferimento è http://www.python.it/.
- Per sviluppatori full-stack: per un programmatore professionista sapere che è
possibile sincronizzare ed equilibrare il frontend e il backend costituiti, ad esempio,
da database e server web che alimentano il frontend delle applicazioni del nostro
sito web, tutto con un unico linguaggio, non ha prezzo.
Anche per questo motivo Python è definito un linguaggio collante, dato che, come
detto, può essere utilizzato per scrivere azioni di backend, consentendo al frontend
e al backend del nostro prodotto digitale di lavorare in armonia. Quindi, se stiamo
cercando di aggiungere competenze lato server o backend al nostro set di
conoscenze come sviluppatore frontend, l'apprendimento di Python potrebbe
essere il perfetto modo per iniziare a farlo.
- Ottimo per l'automazione dei processi: una delle parti essenziali, ma noiose,
del lavoro con i computer è l'amministrazione di tutti i processi ripetitivi, i quali
richiedono tempo per essere espletati. Come, ad esempio, il trascinamento di
cartelle, la copia di file, e la loro ridenominazione, la gestione delle risorse sui
server: tutte queste attività cumulative richiedono molto tempo prezioso. In
questo caso, l'automazione potrebbe esserci di grande aiuto. La capacità di Python

15
di scrivere script di sistema ci consente di creare semplici programmi per
automatizzare attività di routine che in genere richiedono gran parte della nostra
produttività. Il tempo che saremo in grado di risparmiare riadattando i nostri
processi già da solo vale l’impegno nello studio di questo linguaggio.
- Ha gli strumenti per eccellere nel settore tech: l'apprendimento di Python
non solo garantirà la nostra esperienza nello sviluppo di applicazioni su Internet,
ma ci preparerà per una varietà di lavori legati alla tecnologia. Infatti, oltre al
tradizionale sviluppo web, Python è un linguaggio di punta per diversi campi
scientifici emergenti oggi così popolari, tra cui l'analisi dei dati, l'intelligenza
artificiale, l'apprendimento automatico e la scienza dei dati. Pertanto, conoscerlo ci
aiuterà a mantenere aperte le nostre opzioni e avere più opportunità legate al
settore tech.

Detto questo, vale anche la pena segnalare ciò per cui Python non è adatto. Tanto
per cominciare, è un linguaggio di alto livello, quindi non è l'opzione migliore per la
programmazione di sistema, non sarà possibile, ad esempio, gestire driver di
dispositivi o il kernel del sistema. Inoltre, tutto nel linguaggio, comprese le funzioni
e i moduli stessi, vengono gestiti come oggetti, e questo va a scapito della
velocità.
Inoltre, molti programmatori non amano particolarmente il whitespace, cioè lo
spazio che in Python serve per delimitare i rientri e indicare i blocchi. Lo stesso
vale per il duck typing (https://it.wikipedia.org/wiki/Duck_typing), uno stile di
tipizzazione dinamica che potrebbe essere potenzialmente problematico con del
codice lungo e complesso.
Infine, come C#, Java e Go, anche Python ha una sua gestione della memoria
chiamato Garbage Collector, che ci eviterà di tenere traccia degli oggetti presenti
nella ram. Anche se, il suo lavoro avviene automaticamente in background, e
questo potrebbe influire sulle prestazioni, motivo per cui si consiglia di
amministrarlo manualmente o disabilitarlo completamente.

16
L’ambiente di lavoro
Python è multipiattaforma nel senso più ampio del termine. Potrà infatti essere
sfruttato su Windows, su Mac, su Linux e persino sulle piattaforme mobili, Android
e iOS. Di seguito illustreremo come rendere il nostro ambiente di lavoro pronto
all’uso senza troppa fatica.
Per prima cosa occorre segnalare che il sito di riferimento in generale è
https://www.python.org/ e, anche se è tutto in lingua inglese, potremo facilmente
servirci della traduzione online fornita oramai da tutti i principali browser.
- Android: oramai il nostro smartphone ha praticamente sostituito il classico
notebook, quindi anche su dispositivi mobili è possibile fare tutto quello che veniva
eseguito su un PC. L'applicazione che supporta Python pienamente fino alla
versione 3.9 è Pydroid 3. Presenta un ambiente REPL (Read, Eval, Print, Loop),
cioè uno dei più semplici e interattivi che consente anche di modificare, salvare ed
eseguire codice Python. Inoltre, ha un compilatore C, C++ e persino Fortran. Con
esso, potremmo creare qualsiasi libreria da Pip (Python Package Index,
un’incredibile risorsa disponibile a tutti piena di progetti anche complessi) nonché
creare e installare dipendenze da una riga di comando.
- iOS: l'applicazione software Pythonista e Pyto per iOS sono ambienti creativi,
completamente sviluppati, che potremo eseguire sul nostro iPhone o iPad. Hanno
tutte le funzionalità, tra cui un editor, la documentazione tecnica e un interprete, il
tutto integrato in un'unica app.
Pythonista potrebbe essere un ottimo strumento per qualcuno che non ama
particolarmente il laptop e preferisce lavorare o perfezionare le proprie abilità in
movimento. L'applicazione viene fornita con la libreria standard completa di Python
3 e include anche tutta la documentazione utilizzabile senza connessione. Per
usare Pythonista, dovremo solo installarlo dall'App Store.
- Windows: sono tre i modi per installare la distribuzione ufficiale sul sistema
della Microsoft:
- Pacchetto presente sullo Store Microsoft: per i principianti che cercano d’iniziare

17
rapidamente, questo pacchetto è l'opzione migliore per abituarsi al potenziale di
Python. Si può installare in un solo passaggio: cerchiamo Python e selezioniamo la
versione più recente, cioè almeno la 3.9, o successiva. È importante utilizzare la
versione ufficiale, cioè quella della Python Software Foundation, la quale, oltre a
essere gratuita, sarà sempre aggiornata.
- Programma di installazione standard: per gli sviluppatori professionisti, che
cercano opzioni di sviluppo non limitate, è sicuramente consigliata l'installazione
standard fruibile dal sito ufficiale. Consentirà anche una maggiore
personalizzazione.
Dovremo andare su https://www.python.org/, cercando la pagina dei download e
selezionando la versione più recente adatta al nostro pc. Questo perché dalla 3.9
in su il sistema operativo Windows 7 non è più supportato. Oltre a questo dovremo
scegliere tra 32 o 64 bit. Anche in questo caso, se il nostro computer è recente e
ha più di 4GB di memoria ram, converrà sempre orientarsi sui 64 bit.
- Sottosistema Windows per Linux (WSL): WSL consente di eseguire un ambiente
Linux direttamente in Windows. Tutte le informazioni necessarie sono qui:
https://learn.microsoft.com/it-it/windows/wsl/install.
Possiamo anche completare l'installazione su Windows utilizzando piattaforme
alternative, come Anaconda, PyCharm o NumPy le quali sono ottime per fare
calcolo scientifico e data science, cioè lo studio dei dati per ottenerne informazioni
e valori.
Come detto, i due programmi di installazione ufficiali per Windows sono piuttosto
diversi. Il pacchetto presente sullo Store di Microsoft ha limitazioni specifiche. La
documentazione ufficiale afferma che è "destinato principalmente all'uso
interattivo". Ciò significa che il pacchetto è progettato per essere utilizzato da
principianti ed è considerato inadatto per un ambiente di sviluppo professionale.
Inoltre, non dispone dell'accesso completo in scrittura a posizioni condivise come
TEMP o il registro, che potrebbero rappresentare un grosso problema per gli
sviluppatori esperti.

18
MacOS
La seconda edizione di Python e altri linguaggi di programmazione come Ruby e
Perl, un tempo erano preinstallati nelle varie versioni di MacOS. Tuttavia, non è più
il caso di quelle correnti, a partire da MacOS Catalina del 2019. Quindi dovremo
procedere manualmente. Esistono due metodi di installazione: tramite il
programma ufficiale oppure utilizzando il gestore di pacchetti Homebrew. Nel
primo caso dovremo semplicemente scaricare il programma di installazione dal sito
https://www.python.org/, mentre nel secondo effettueremo prima il download e
l'installazione del gestore di pacchetti, tenendo presente che non è supportato
dalla Python Foundation. Per questo motivo su MacOS il metodo più sicuro è
ancora quello di utilizzare il programma e le sue edizioni ufficiali.

Installare Python su Linux


Di solito, molte distribuzioni Linux vengono già fornite con Python, anche se
probabilmente non saranno aggiornate all’ultima versione. In tal caso dovremo
fare per conto nostro. Esistono due modi per installare la distribuzione ufficiale sul
nostro Linux:
- dal gestore di pacchetti: è il metodo di installazione più diffuso nella maggior
parte delle distribuzioni Linux. Richiede l'esecuzione di istruzioni dalla linea di
comando.
- dal codice sorgente: è più complesso rispetto al precedente. Implica l'esecuzione
di una serie di comandi dalla riga di comando e allo stesso tempo l'assicurarsi di
avere le dipendenze corrette installate per completare il codice sorgente di Python.
Sfortunatamente, non tutte le distribuzioni Linux hanno un gestore di pacchetti e
non tutti i gestori di pacchetti hanno Python nel loro set. Quindi, in base al nostro
sistema operativo, costruire Python dal codice sorgente potrebbe essere l’unica
opzione disponibile.
Per completare l'installazione su Linux, abbiamo diverse possibilità in base al
nostro sistema:

19
- Arch Linux: è famoso per aderire al principio KISS (Keep It Simple, Stupid),
principalmente focalizzato su modernità e pragmatismo. Inoltre tiene il passo con
le versioni di Python. È molto probabile che abbiate già l'ultima versione installata
per impostazione predefinita. In caso contrario, dovrete aggiornarlo.
- CentOS e Fedora: sfortunatamente, Python 3.9 non è disponibile nei loro archivi,
dovremo quindi compilare Python dal codice sorgente.
- Debian Linux: prima di poter installare Python 3.9 su Debian, dovremo installare
il comando sudo (super user do).
- openSUSE: compilare dal sorgente è il modo più affidabile per configurare Python
su questo sistema. Per farlo, dovremo installare gli strumenti di sviluppo,
operazione che può essere eseguita nella configurazione YaST del sistema
operativo tramite i menu o utilizzando Zipper.
Poiché dovrà installare più di 150 pacchetti, potrebbe richiedere un po' di tempo
per essere completata.
- Ubuntu e Linux Mint: a seconda della versione della distribuzione Ubuntu che
abbiamo, il processo per configurare Python varierà di conseguenza:
- Ubuntu 18.04, 22.04 e successive: Python 3.9 non viene fornito per
impostazione predefinita su Ubuntu 18.04 e versioni successive, ma è disponibile
nell’archivio (repository) Universe.
- Ubuntu 17 e Linux Mint precedenti: Python 3.9 non è nel repository Universe,
quindi dovremmo ottenerlo da un Personal Package Archive (PPA).

Strumenti (Tools) di lavoro


Anche se, teoricamente, per scrivere del codice con Python, potremmo avvalerci
solamente del fidato blocco note, quando scriveremo dei listati più complessi,
avremo certamente bisogno degli strumenti giusti per lavorare bene.
A tal proposito, dovremo imparare a distinguere tra un IDE (ambiente di sviluppo
integrato) e un semplice editor di testo, infatti, il primo ci permetterà, oltre alla

20
semplice stesura del listato, la creazione vera e propria di un programma a sé
stante, attraverso la compilazione, la quale viene eseguita dal bytecode compiler,
che traduce il codice sorgente Python in bytecode, un formato di codice più
compatto ed efficiente che viene eseguito dalla Python Virtual Machine (PVM).
La compilazione del codice può migliorare le prestazioni del programma in quanto
verrà eseguito più rapidamente rispetto a quello interpretato. Inoltre, può
proteggere il codice sorgente da occhi indiscreti, in quanto il bytecode compilato è
più difficile da leggere e capire rispetto al codice sorgente. Tuttavia, la
compilazione del codice non è necessaria per l'esecuzione del programma, dato
che Python è un linguaggio interpretato. Quindi, la scelta dipenderà dalle
specifiche esigenze del progetto e delle prestazioni richieste.
Nelle pagine successive, verrà presentata una panoramica dei principali strumenti
gratuiti (se possibile) utilizzati per scrivere, correggere e compilare codice in
Python.

Editor di testo e Linter


Esistono diverse alternative al blocco note con degli editor di testo più evoluti. Tra
questi, ne sono disponibili tre molto validi: Notepad++, Visual Studio e Atom.
Notepad++ è un editor gratuito che può essere scaricato dal sito ufficiale
https://notepad-plus-plus.org/. E’ caratterizzato da un'interfaccia semplice e di
facile utilizzo e offre numerose funzionalità utili, tra cui l'evidenziazione della
sintassi, la possibilità di raggruppare porzioni omogenee di codice (detta Syntax
Folding) per nascondere o visualizzare porzioni di un documento lungo,
l'evidenziazione delle parentesi, la ricerca e la sostituzione mediante l’uso di
espressioni regolari, l'auto completamento della sintassi, la possibilità di
aggiungere segnalibri e la visualizzazione a schede, nonché la possibilità di esporre
documenti affiancati per il confronto.
Visual Studio Code è editor di testo gratuito sviluppato da Microsoft per diverse
piattaforme, tra cui OS 12, Windows e Linux. Questo editor può essere scaricato

21
dalla pagina ufficiale https://code.visualstudio.com/. Dispone già di Git ed è
integrabile con ulteriori pacchetti. Tra i suoi punti di forza troviamo l'auto
completamento, l'evidenziazione della sintassi, la funzionalità di ricerca e
sostituzione, la possibilità d’impostare breakpoint, la capacità di lavorare
direttamente con file e cartelle senza la necessità di creare progetti.
Atom è un altro editor di testo gratuito e multipiattaforma, scaricabile dal sito
https://atom.io/. Potremo implementarlo con diversi pacchetti open source e offre
anche il supporto al sistema di controllo della versione Git. Tra i punti di forza di
Atom troviamo l'auto completamento, l'evidenziazione della sintassi, la funzionalità
di ricerca e sostituzione tra più file e la possibilità di aprire diversi file in pannelli
affiancati per poterli confrontare.
Infine, per evidenziare e correggere eventuali errori di sintassi, di runtime o di
progettazione, oltre agli strumenti inclusi in un IDE, esiste il cosiddetto Linter, il
quale è un programma che in genere si integra con un editor. Una buona scelta
gratuita è Pylint (https://pypi.org/project/pylint/).
Oltre agli editor di testo menzionati, negli appstore dedicati di Windows, MacOS,
iOS e Android esistono numerosi editor gratuiti anche in prova, più o meno validi,
comodi e veloci, da utilizzare per esercitarsi e studiare. È quindi consigliabile dare
un'occhiata ai vari software disponibili per trovare quello che meglio soddisfa le
proprie esigenze.

Linguaggio macchina
Il linguaggio di programmazione, noto anche come codice macchina, è la lingua
che i computer leggono quando viene lanciata un'applicazione, analogamente alla
lingua giapponese per i giapponesi. Esso è costituito da sequenze di uno e zero.
La programmazione si divide in linguaggi di basso livello e di alto livello, a seconda
di quanto questi siano vicini al codice macchina. I primi si avvicinano di più al
codice macchina, mentre i secondi sono più vicini al linguaggio umano. Ad
esempio, Python e Java sono considerati linguaggi di alto livello, mentre C++

22
rappresenta un linguaggio di livello medio. L’evoluzione è andata in questa
direzione per facilitare l’avvicinamento al mondo della programmazione e per
permettere agli sviluppatori di concentrarsi sulla propria idea senza dover dedicare
tempo e risorse alla gestione della memoria ram o del processore, come accadeva
nei linguaggi di basso livello, per esempio l'Assembly.
È anche bene ricordare che i computer non sono in grado di eseguire direttamente
i linguaggi di programmazione. Alcuni, come Java e Python, sono interpretati, il
che significa che ogni riga di codice viene interpretata in tempo reale. Tuttavia,
questo processo rallenta l'esecuzione rispetto alla compilazione, dove il codice
viene tradotto in linguaggio macchina prima dell'esecuzione. Il processo di
compilazione è automatizzato e viene eseguito da un ambiente di sviluppo.
Una volta che il programma è stato compilato, potrebbe essere eseguito solo
nell'ambiente in cui è stato creato, ad esempio solo su Windows, su Linux o
MacOS. Inoltre, la compilazione non rende il programma completamente
indipendente, in quanto gli assembly contengono altre risorse utilizzate dal
programma, come i dati dell'immagine e i metadati del progetto.

IDE
Come detto, per poter programmare in maniera seria non potremo fare a meno di
un IDE (Integrated Development Environment), cioè di un ambiente di sviluppo
integrato. In poche parole stiamo parlando di un programma che ci servirà per
scrivere un listato, per testarlo ed eventualmente compilarlo, per renderlo
eseguibile e indipendente. Di seguito ne elencheremo alcuni utili per i nostri
esperimenti e per la realizzazione delle applicazioni.
Sono ottime opzioni: PyCharm (https://www.jetbrains.com/pycharm/), PyDev
(https://www.pydev.org/).
Non dimentichiamoci di scaricare il codice sorgente da: https://www.python.org/.
In conclusione vorrei segnalare che, anche se di questi software per l’ambiente di
sviluppo ne esistono tantissimi altri, più o meno validi, inizialmente non converrà

23
perdere troppo tempo sulla loro scelta, soprattutto per chi è alle prime armi. In
seguito, con l’esperienza o il consiglio di qualche collega, potremmo sbizzarrirci per
sperimentarne altri.
Chi avesse dei problemi con la lingua inglese potrà sempre servirsi di Google
Translate o di qualsiasi altra applicazione simile integrata nel browser.

Server virtuale
Qualche volta nei nostri progetti, potremmo avere la necessità di emulare un
server. Per cominciare andrà benissimo servirsi di un servizio open surce e gratuito
come quello proposto da XAMPP, su: https://www.apachefriends.org/it/index.html.
L’alternativa è quella di orientarsi verso un software più professionale, ma a
pagamento. Potremo sempre decidere di farlo in un secondo momento.

Intelligenze Artificiali (IA)


Negli ultimi due anni, ovvero nel 2022 e nel 2023, questa tecnologia è stata
consacrata all'uso del grande pubblico. A oggi esistono molte opzioni disponibili
gratuitamente, alcune specializzate solo in determinati ambiti, come ad esempio la
creazione di video, di immagini o la guida autonoma. Quasi tutti hanno
sicuramente provato e sperimentato il fantastico giocattolo di OpenAI, cioè
ChatGPT (Generative Pretrained Transformer), che è un sistema di apprendimento
pre-addestrato generativo. Esso si serve di una rete neurale e di algoritmi
complessi di deep learning per comprendere e generare testo di risposta in base
all'input dell'utente.
Questa tecnologia è utile anche nel nostro ambito, poiché grazie a ChatGPT è
possibile avere un dialogo costruttivo su argomenti legati alla programmazione e
anche ottenere del codice come esempio. In alcuni casi, ChatGPT potrebbe
sostituire un tutor o risolvere una situazione in cui un listato crea dei problemi.
Oltre a ChatGPT, ci sono anche altre opzioni disponibili, tra cui sicuramente Bard di
Google, lanciato agli inizi del 2023 negli Stati Uniti e in altri paesi, sarà disponibile

24
in italiano entro la fine dell’anno. Consigliamo di dare un'occhiata ad entrambi, ma
anche a tutte quelle che seguiranno.

UML
Per progetti software più grandi, è spesso consigliabile utilizzare la Universal
Modeling Language (UML) per visualizzare i software orientati agli oggetti. I
diagrammi di classe sono il tipo più noto e diffuso di diagramma UML, i quali
rappresentano le classi attraverso un rettangolo diviso in tre parti contenenti il
nome della classe, le sue variabili istantanee e i suoi metodi. Le connessioni tra le
classi sono presentate in un diagramma, contrassegnate da una linea continua per
le associazioni e una linea continua con una punta di freccia vuota e chiusa per
l'ereditarietà. Inoltre, ci sono due tipi importanti di connessioni tra le classi: la
dipendenza e il contenimento, contrassegnati rispettivamente da una linea
tratteggiata e un rombo. La forza del contenimento è segnata da un rombo pieno o
vuoto, a seconda che la connessione sia rispettivamente permanente
(aggregazione) o temporanea (composizione). Tutto ciò potrebbe non dirci molto,
ma forse, dopo aver studiato l’argomento, cambieremo idea, e allora è bene
sapere che sul Web ne esistono moltissime versioni, alcune gratuite, come ad
esempio StarUML, https://staruml.io/download o UMLet, https://www.umlet.com/.
Non è un software imprescindibile per apprendere Python, tuttavia, come già
detto, potrebbe rivelarsi molto utile, dedicategli qualche minuto del vostro tempo.

Debug, Boilerplate code e convenzioni


Correggere gli errori all'interno dei nostri programmi può essere una parte
estremamente frustrante del processo di sviluppo software. Tuttavia, è un
passaggio essenziale per garantire che il nostro codice funzioni correttamente.
Questa fase di correzione degli errori è chiamata debugging e consiste
nell'identificare e risolvere gli errori presenti nel codice.
Esistono diversi metodi per eseguire il debugging, a seconda delle necessità e della

25
complessità del progetto. Uno dei metodi più comuni è il tracciamento del codice.
Questo metodo prevede di visualizzare sullo schermo i risultati di ogni riga di
codice eseguita e prestare molta attenzione al contenuto delle variabili e di altre
strutture di dati, poiché sono alterate durante l'esecuzione del programma. Il
tracciamento funziona bene per i progetti più piccoli, come i tutorial all'interno di
questo libro.
Un altro approccio comune al debugging è il metodo di registrazione e
riproduzione. In questo caso, parti dell'esecuzione del programma vengono
registrate e riprodotte per esaminare i potenziali difetti. Questo metodo si
concentra sui procedimenti a livello statale all'interno del programma, piuttosto
che sulla riproduzione esterna o visiva del software.
Un altro metodo utilizzato comunemente è il debugging post-mortem. Questo
metodo consiste nell'analisi dei dati del registro post-arresto anomalo su un
programma. Molti tipi di software scrivono file di registro su disco dopo gravi
malfunzionamenti, inclusa la maggior parte dei sistemi operativi. Questi file
possono quindi essere esaminati alla ricerca d’indizi su quali bug hanno causato un
arresto anomalo.
Il debugging da remoto è un approccio utile quando il programma viene eseguito
su un dispositivo separato rispetto alla macchina di sviluppo. Utilizzando i metodi
di rete più diffusi, come il Wi-Fi o il cablaggio USB, è possibile collegare dispositivi
con ruoli e fattori di forma diversi per lavorare insieme. Questo è l'approccio più
comune durante la scrittura e il debugging di software per Android e iOS.
In caso di una quantità particolarmente grande di bug, può essere utile utilizzare
l'approccio del raggruppamento. Il programmatore identifica tutte le caratteristiche
comuni nei bug e li classifica in gruppi specifici che ne condividono gli attributi. La
logica è che, anche se alcuni bug vengono risolti all'interno di un cluster, il resto
dovrebbe seguire.
Inoltre, prestare attenzione alla parte estetica del codice è altrettanto importante.
La ripetizione di parti che sembrano non cambiare è meglio definita in inglese

26
come boilerplate code. In alcuni casi, è possibile risolvere il problema con una
aggiustatina al codice, ma a volte non sarà possibile eseguire il cosiddetto
refactoring. Pertanto, fin dalle prime fasi del processo di sviluppo, è importante
fare attenzione al boilerplate code per evitare di dover riscrivere l'intero
programma da capo.
È utile sottolineare che, spesso nel libro, ci serviremo del termine client per
indicare colui che utilizza una determinata funzionalità del programma. Tale
termine viene utilizzato come convenzione per rendere più chiaro e semplice il
linguaggio utilizzato nel testo e facilitare la comprensione degli argomenti trattati.
L'utilizzo di convenzioni è un elemento importante nella scrittura di codice, poiché
permette di creare un linguaggio comune tra i membri del team di sviluppo e tra
gli utenti del software. Le convenzioni riguardano non solo la scelta dei termini da
utilizzare, ma anche la formattazione, l'indentazione, la gestione degli errori e
molte altre pratiche che consentono di scrivere codice più leggibile, mantenibile e
affidabile.
Nel contesto specifico del termine client, è importante specificare che esso può
assumere diversi significati a seconda dell’ambito in cui viene utilizzato. Ad
esempio, nel contesto delle reti informatiche, client viene utilizzato per indicare un
computer o un dispositivo che richiede l'accesso a una risorsa o un servizio offerto
da un altro computer o dispositivo, cioè il server. Mentre, nel contesto del
software, il termine client può essere utilizzato per indicare l'interfaccia utente di
un'applicazione, ovvero quella parte del software con cui l'utente interagisce
direttamente. Ad esempio, in un'applicazione per la gestione di un negozio online,
il client corrisponderebbe alla pagina web o all'app mobile attraverso cui l'utente
effettua gli acquisti. In conclusione, ci serviremo del termine client per semplificare
la comunicazione e rendere più chiaro il linguaggio utilizzato nella scrittura di
codice. Ovviamente faremo attenzione al contesto in cui lo utilizzeremo per evitare
eventuali ambiguità o fraintendimenti.

27
Commentare il codice
Durante il nostro studio, ci troveremo ad avere dei listati da interpretare o
semplicemente da copiare. In molti di questi saranno presenti dei commenti per
aiutarci a comprendere meglio il metodo, la sintassi, la classe o la porzione di
codice in esame. Ogni linguaggio, per permettere d’integrare del testo avulso dal
codice, ha una sua grammatica specifica, in Python dovremo utilizzare il simbolo
del cancelletto # prima del commento.
Il testo che si trova dopo il carattere # fino alla fine della riga è chiamato
commento. Può essere utilizzato per inserire qualsiasi informazione. Un commento
è un messaggio al lettore del codice sorgente e può aiutare nella comprensione del
programma. Se i nomi delle variabili nel programma sono scelti correttamente, è
necessaria una quantità minima di commenti.
I commenti multilinea possono essere ottenuti con le stringhe multilinea. Le
stringhe su più righe iniziano e terminano con una virgoletta tripla (in genere """)
e le interruzioni di riga sono consentite all'interno della stringa. Tecnicamente
questi non sono commenti, ma espressioni. Pertanto, i commenti su più righe
devono sempre iniziare in una nuova riga separata:

# questa riga è solo un commento


""" queste righe formano un commento
multilinea """

Una funzione può iniziare con una cosiddetta stringa di documentazione detta
docstring, che fornisce una descrizione della funzione e può essere interrogata. La
stringa di documentazione è un commento su più righe che contiene una breve
descrizione dell'attività della funzione su una riga, una sua descrizione dettagliata
separata da una riga vuota e infine una descrizione dei parametri dopo la parola
args.

28
29
# 1 - Le prime basi

Costanti e variabili
Tipi di dati
Le iterazioni
Gli operatori
Typecasting

Come accennato nella sezione introduttiva, Python è caratterizzato da una sintassi


relativamente semplice e facilmente leggibile. In particolare, ogni istruzione è
separata solamente da una nuova riga e non richiede l'uso del classico punto e
virgola usato in altri linguaggi, come ad esempio JavaScript o Java. Inoltre,
l'ambito di definizione delle funzioni o dei loop è delimitato dall'utilizzo dello spazio
bianco o whitespace, comunemente noto come indentazione, al posto delle
parentesi graffe usate in altri linguaggi. Ed è proprio questa caratteristica che
rende Python differente, più leggibile e immediatamente comprensibile.
Questa è una delle ragioni per cui viene utilizzato, ad esempio, per l'analisi dei
dati, che oggi è diventato un settore molto importante. Molte aziende cercano
professionisti in grado di gestire enormi database e prevenire errori inattesi. Un
buon analista di dati deve essere in grado di utilizzare vari strumenti di
programmazione per gestire grandi quantità di dati complessi e ricavarne
informazioni rilevanti per l’azienda o per il business. E Python è perfetto proprio
per queste cose. Quindi non perdiamo altro tempo e iniziamo!

Le costanti e le variabili
In qualsiasi programma andremo a creare, avremo bisogno di elaborare e gestire
alcuni dati. Questi possono essere costanti o variabili. Le costanti sono un tipo di

30
dato non modificabile durante l'esecuzione del programma, al contrario delle
variabili ovviamente. Entrambe rappresentano una forma di archiviazione
temporanea che utilizza la memoria ad accesso casuale (RAM) del dispositivo
utilizzato. Quando lo spegneremo i dati scompariranno, a meno che non siano stati
salvati su un dispositivo di archiviazione.
Le variabili possono essere utilizzate per una vasta gamma di scopi, come
memorizzare il nome di un giocatore in un videogame, eseguire calcoli complessi o
visualizzare un grafico. Esistono diversi tipi di variabili e costanti in ogni linguaggio
di programmazione, come stringhe di testo, caratteri alfanumerici e valori
numerici. La distinzione tra i diversi tipi di dati è importante perché consente di
utilizzare la memoria del dispositivo in modo efficiente.
In Python, la dichiarazione delle variabili e delle costanti è semplificata rispetto ad
altri linguaggi come Java o C#. Spesso, non è necessario specificare il tipo di dato
per le variabili, poiché viene automaticamente definito al momento della
dichiarazione. Esistono inoltre diversi tipi di dati incorporati che sono
estremamente utili per la scrittura di codice ben strutturato. Ogni oggetto ha
un'identità, un tipo e un valore, con i primi due fissi e il valore che può essere
mutabile o immutabile, a seconda del tipo di dato.
Le variabili in Python possono memorizzare diversi tipi di dati senza la necessità di
essere definite esplicitamente quando vengono istanziate. In poche parole, gli
oggetti numerici vengono creati solo quando si assegna loro un valore. Per
esempio:

variabile = 1

I nomi delle variabili in Python dovranno:

- Contenere solo lettere, numeri o il carattere di sottolineatura _.


- Iniziare con una lettera.

31
- Non contenere spazi o punteggiatura.
- Non essere racchiusi tra virgolette o parentesi.

Per le costanti è tutto più facile: dovranno solamente essere dichiarate in lettere
maiuscole:

LIBRI = 10

Tipologia di variabili
In tutti i linguaggi di programmazione la variabile è una posizione all’interno della
memoria di un dispositivo (smartphone o computer) in cui archivieremo dei dati. È
composta dal nome, dal valore e ha sei tipologie differenti: set, dizionari, numeri,
stringhe, liste e tuple. Andiamo a vedere le varie tipologie che poi approfondiremo
nel capitolo:

- Numeri: che possono essere positivi, negativi, in virgola mobile o complessi.


- Stringhe: sono complessi di caratteri vari o alfanumerici racchiusi da virgolette.
Andranno bene sia quelle singole che le doppie: cuoco = "Roby"
- Set: è un insieme di valori non ordinati, racchiusi utilizzando parentesi graffe. I
valori duplicati non verranno valutati: numeri = {1, 2, 5, 7, 15}
- Dizionari: sono composti da coppie chiave-valore non ordinate, anch’essi inseriti
tra le parentesi graffe: pranzo = {'primo': 'pasta', 'peso': 120}
- Liste: sono costituite da valori o variabili di qualsiasi tipo. Racchiuse da parentesi
quadre. Le stringhe hanno le virgolette singole: lista = [5, 'pane', 2, 'Olio']
- Tuple: sono insiemi simili alle liste, ma di sola lettura che quindi non potremo
modificare. Andranno inserite tra parentesi: tupla = (10, "Polpette")

Operatori e metodi
Con operatori ci riferiamo ad alcuni simboli matematici essenziali ad esprimere

32
diversi concetti in maniera diretta. Per adesso elencheremo quelli usati
maggiormente, più avanti nel capitolo torneremo sull’argomento per approfondirlo:

Operatore Utilizzo Esempio

= Attribuisce a una variabile un valore x = 3


== Stabilisce l’uguaglianza if x == 3...
!= Stabilisce la differenza if x != 3...
< > Stabiliscono maggiore o minore if x < 3...
>= <= Come sopra con in più l’uguaglianza if x >= 3...

Andiamo a vedere come si comportano gli operatori nel concreto:

print('Inserisci il tuo peso:')


Peso = input()
Peso = int(Peso)
if Peso >= 70: print('Fai attenzione alla dieta!')
if Peso < 70: print('Ogni tanto mangia...')

I metodi sono parte della programmazione OOP e ci serviranno per eseguire


operazioni con gli oggetti o le istanze delle classi. Anche se si tratta di argomenti
che tratteremo ampiamente più avanti, è bene introdurne il concetto dato che ne
parleremo spesso. Essenzialmente sono funzioni o istruzioni specifiche che
permettono alle nostre applicazioni di usufruire dei vantaggi nell’utilizzo degli
oggetti software. Si dividono in:
- metodi astratti, come per le classi astratte sono utili per l’ereditarietà.
- metodi di istanza, sono tutti quei metodi invocati con riferimento ad un oggetto.
- metodi statici, in questo caso non si riferiscono a un solo oggetto ma a tutta la
classe.

33
- metodi dunder, o anche metodi magici, adoperabili sempre con le classi e le
istruzioni derivate, tutte contraddistinte dal carattere underscore _ (es. __iter__).
- costruttori, usati per evocare un nuovo oggetto.

I dati
Come già detto, ogni listato elabora diversi tipi di dati. Queste operazioni,
tecnicamente, si svolgono all'interno della memoria dei nostri smartphone o
computer. I dati sono espressi in byte, in gruppi di otto bit con una grandezza che
va da 0 a 255. I byte possono essere utilizzati insieme per rappresentare valori più
grandi, come ad esempio il megabyte (un milione di byte) e il gigabyte (un
miliardo di byte), oramai disponibili su tutti i nostri dispositivi.
A ogni byte possiamo assegnare un valore, ad esempio un numero o una stringa.
Gli standard, come ASCII per il testo e JPG per le immagini, derivano
essenzialmente da questo concetto.
Ogni linguaggio di programmazione ha i propri tipi di dati, che di solito consistono
in valori booleani (vero o falso), numeri interi o in virgola mobile e stringhe.
Esploreremo tutti questi tipi di dati più avanti. Inoltre, gli oggetti (come una
porzione di codice) e le collezioni di dati (come gli insiemi) sono anch'essi tipi di
dati che esploreremo nei prossimi listati.
A differenza di altri linguaggi di programmazione, Python non ha una struttura
rigida per la fase di dichiarazione delle variabili. Questo significa che le variabili
possono essere definite in qualsiasi punto, ma il programma interpreta in maniera
automatica il tipo di dato numerico inserito da tastiera.
Tutti i tipi di dati sono memorizzati nella memoria del computer per essere
elaborati. Alcuni di questi valori potranno essere modificati durante l'esecuzione,
ma una volta creati nella memoria, il loro contenuto non potrà essere alterato. I
numeri, le stringhe e le tuple sono immutabili, il che significa che il loro contenuto
non può essere modificato dopo la creazione, mentre le collezioni come i dizionari,
le liste e gli insiemi sono mutabili.

34
Python supporta due tipologie principali di dati incorporati, dati numerici e
sequenze di dati:

1) Dati numerici suddivisi a loro volta in:

- Numeri interi (int): indicano numeri interi positivi o negativi (senza parte
frazionaria o decimale). In Python, le variabili intere vengono decise assegnando
un numero intero a una variabile. La funzione type() può essere utilizzata per
definire il tipo di dati di una variabile, infatti, nel prossimo esempio, il suo output
(class 'int') indica che la variabile x è un numero intero, il quale può essere anche
negativo o uguale a zero:

x = 1
y = -5
w = 0

print(type(x)) # <class 'int'>


print(type(y)) # <class 'int'>
print(type(w)) # <class 'int'>

- Numeri in virgola mobile (float): sono un altro tipo di dati rappresentato da


decimali, positivi, negativi e zero. Inoltre, i float possono anche essere
rappresentati da numeri in notazione scientifica, per numeri molto grandi o che
contengono esponenti. Durante la scrittura, il float può essere definito utilizzando
un punto decimale quando viene assegnata una variabile. Inoltre potremo definire
i float in notazione scientifica usando sia una e minuscola che una E maiuscola:

x = 2.5
y = -0.5
w = 3.17E10
35
Z = 3.17e10
print(type(x)) # <class 'float'>
print(type(y)) # <class 'float'>
print(type(w)) # <class 'float'>
print(type(Z)) # <class 'float'>

Nel caso in cui si voglia definire una variabile come float invece che come intero,
come accennato, basterà aggiungere un punto decimale finale, anche se alla
variabile è assegnato un numero intero:

x = 1
y = 1.
print(type(x)) # <class 'int'>
print(type(y)) # <class 'float'>

- Numeri interi lunghi (long): sono numeri interi di dimensione illimitata, scritti
come numeri interi ma seguiti da una L maiuscola o minuscola. Usati ancora in
Python 2, sono deprecati nell’ultima versione.

- Numeri complessi (complex): un altro tipo di dati numerici utili per risolvere
problemi definiti. Utilizzano un numero reale + un componente immaginario j che
in pratica rappresenta la radice quadrata di -1, cioè un numero immaginario. Vale
anche la pena ricordare che i numeri complessi non sono molto usati nella
programmazione Python:

x = 5 + 9j
print(type(x)) # <class 'complex'>

- Valore nullo (NoneType): rappresenta l'oggetto nullo o viene utilizzato per


funzioni che non restituiscono esplicitamente un valore.
36
x = None
print(type(x)) # <class 'NoneType'>

2) Sequenze di dati ossia le stringhe, le raccolte (gli insiemi) e i boleani:

- Stringa: la sua definizione più elementare può essere una sequenza di caratteri,
in cui sequenza è un tipo di dati composto da diversi elementi dello stesso genere,
ad esempio interi, float, caratteri, stringhe, ecc. In Python esiste un codice univoco
fornito a tutti i caratteri esistenti. La convenzione di codifica era stata etichettata
come formato Unicode. Consiste di caratteri di quasi tutte le lingue possibili e
anche di emoticon. Quindi, le stringhe possono essere considerate come un tipo
speciale di sequenza, in cui tutti i suoi elementi sono caratteri. Ad esempio, la
stringa "Mario" è fondamentalmente una sequenza ['M', 'a', 'r', 'i', 'o'] e la sua
lunghezza può essere calcolata contando il numero di caratteri all'interno della
sequenza.
La gestione delle stringhe in Python richiede il minimo sforzo perché la maggior
parte delle operazioni sulle stringhe ha una complessità molto bassa rispetto ad
altri linguaggi. Esistono diversi modi per gestire le stringhe, vi mostriamo i tre
principali:

- Concatenazione: Concatenazione significa semplicemente unire due stringhe. Ad


esempio unire "Mar" con "io", per ottenere "Mario":

print("Mar" + "io")

Non ci sono particolari limiti alla concatenazione:

a = "Mario "

37
b = " la birra "
c = " adora "
d= " e odia "
e= " l acqua"

print(a + c + b + d + e)

- Ripetizioni: per replicare lo stesso testo, ad esempio "Mario" trenta volte,


potremmo agire manualmente, perdendo un sacco di tempo e creando del codice
orrendo, oppure potremo usare un po’ di logica:

print("Mario"*30)

Se vogliamo che l'utente inserisca un numero e in base a questo, il testo venga


stampato sulla console n volte possiamo semplicemente creare una variabile n e
usare la funzione input() per ottenere un numero dall'utente e poi moltiplicarlo:

n = input("Quante volte ripetere la parola Mario? ")


print("Mario"*int(n))

- Slice (affettare): consente di estrarre una parte di una stringa in base a un indice
iniziale e uno finale. Ad esempio, se vogliamo estrarre solo un carattere, allora
possiamo utilizzare una delle seguenti sintassi:

stringa="Mario"
print(stringa[1:4:1]) # ari

Il risultato sarà ari, perché nelle istruzioni tra parantesi abbiamo indicato di partire
dalla posizione 1 (M=0, a=1, r=2, i=3, o=4) fino alla 4, con un passo di 1 solo
carattere. Gli ultimi due parametri sono facoltativi.
38
Inoltre, con le stringhe, abbiamo a disposizione oltre quaranta funzioni e metodi
integrati per ottenere svariati risultati. Ricordiamo che la differenza tra metodo e
funzione essenzialmente è che il primo è associato a un oggetto, al contrario della
funzione che ne è indipendente. Ora elencheremo i metodi principali delle stringhe,
tenendo conto che gli esempi si basano sulla stringa uno = "1 Ciao da Mario":

Metodo Utilizzo Esempio

format() Inserisce valori nel segnaposto {} a='{}'.format(1)


find() Riporta la posizione del testo uno.find('Mario')
isalpha() True se nella stringa solo caratteri uno.isalpha()
isalnum() True se nella stringa solo alfanum. uno.isalnum()
isnumeric() True se nella stringa solo numeri uno.isnumeric()
isspace() True se nella stringa solo whitespace uno.isspace()
join() Riporta gli elementi da array a stringa uno.join(...)
lower() Riporta il testo in minuscolo uno.lower()
replace() Sostituisce un elemento con l’altro uno.replace(1,2)
split() Divide la stringa in una lista uno.split()
strip() Rimuove gli spazi iniziali e finali uno.strip()
upper() Riporta il testo in maiuscolo uno.upper()

Per approfondire l’argomento: https://docs.python.org/3/library/string.html.

- Booleani: questo tipo di dato, in poche parole, può essere vero (True) o falso
(False). Nonostante a prima vista sembrino banali, in realtà sono molto utili per
gestire il flusso del nostro codice, e lo scopriremo nel corso dei capitoli. Vediamo
un semplice esempio per intuirne le possibilità:

1 == 1 # True

39
if 1 == 1: print("prova ok")
else: print("problema in vista")

Le raccolte di dati
Per gestire e lavorare con le varie tipologie di dati viste finora, come in tutti i
linguaggi di programmazione, avremo a disposizione le raccolte o insiemi,
conosciuti anche come array. In Python vi sono delle differenze sostanziali. Senza
entrare troppo nello specifico, il classico array in Python non esiste.
Essenzialmente vi sono quattro tipologie differenti di sequenze dati: i set, le liste,
le tuple e i dizionari. Vediamoli nel dettaglio:

Il set
È una raccolta di dati, o meglio, di elementi univoci. Non è consentito duplicare gli
elementi del set. Il contenuto può essere modificato, ma gli elementi al suo interno
no. I set non sono ordinati. Tuttavia, è molto facile lavorarci. Possiamo creare un
set in due modi, con la funzione incorporata set(), o con le parentesi graffe:

a = set(('Mario', 'Roby'))

a = {1, 'Mario', -1, 'Roby'}

Molte delle operazioni standard sui dati che possono essere utilizzate per gli altri
non funzionano allo stesso modo per gli insiemi. Ad esempio, i set non possono
essere indicizzati o tagliati. Ma allo stesso tempo, Python fornisce tutta una serie di
operazioni sugli oggetti insiemi che generalmente imitano le operazioni definite per
gli insiemi matematici. La maggior parte di queste operazioni può essere eseguita
in due modi diversi: dall'operatore o da un metodo. Possiamo vedere come
funzionano usando il metodo union come esempio. Unendo due raccolte, uno e
due, otterremo un insieme costituito da tutti gli elementi di entrambi:

40
uno = {'Mario', 'birra', 'pizza'}
due = {'Mario', 'Roby', 'Pino'}
tre = uno.union(due)
print(tre)

Risultato: {'Mario', 'birra', 'pizza', 'Roby', 'Pino'}

L'elemento "Mario", presente sia in uno che in due, appare solo una volta
nell'unione perché, come abbiamo detto prima, gli insiemi non contengono mai
valori duplicati.
Un altro modo per associare i set è con l’operatore |:

uno = {'Mario', 'birra', 'pizza'}


due = {'Mario', 'Roby', 'Pino'}
uno | due

Come visto in questi esempi, l'operatore e il metodo si comportano in modo quasi


identico. Tuttavia, c'è ancora una sottile differenza tra loro. Quando usiamo
l’operatore |, entrambi gli operandi devono essere impostati. Mentre il metodo
union(), prenderà qualsiasi iterabile come argomento, convertendolo in un set, per
poi eseguire l'unione.
E anche se gli elementi contenuti in un insieme devono essere del tipo immutabile,
gli insiemi stessi possono effettivamente essere modificati. Esiste un mix di
operatori e metodi che possono essere implementati per modificare il contenuto di
un set. Pertanto, ciascuno degli operatori di unione, intersezione, differenza e
differenza simmetrica sotto elencati ha una forma di assegnazione aumentata che
può essere applicata per modificare un insieme. E per ciascuno di essi esiste anche
un metodo corrispondente:

41
Metodo Utilizzo Esempio

add() Aggiunge un elemento al set uno.add('Maria')


clear() Rimuove tutti gli elementi dal set uno.clear()
copy() Copia un set tre = uno.copy()
difference() Set ottenuto dalla differenza di altri due c=a.difference(b)
discard() Rimuove un elemento (senza errore) uno.discard(...)
intersection() Set ottenuto dall’intersezione di due c=a.intersection(b)
pop() Rimuove un elemento a caso uno.pop()
remove() Rimuove un elemento uno.remove(...)
union() Unione tra più set c = a.union(b)
update() Aggiunge elementi di altri set a.update(b)

Oltre a questi vi sono dei metodi aggiuntivi per delle operazioni molto specifiche:

- difference_update() rimuove da un set gli elementi inclusi in un altro:

uno.difference_update(due) # {'birra', 'pizza'}

- intersection_update() il contrario del metodo precedente, rimuove da un set


gli elementi non inclusi in un altro:

uno.intersection_update(due) # {'Mario'}

- symmetric_difference() ritorna un nuovo set con tutti gli elementi degli altri
due meno quelli doppi:

tre=uno.symmetric_difference(due) # {'birra', 'pizza', 'Roby', 'Pino'}

42
- symmetric_difference_update() aggiorna un set con tutti gli elementi del
secondo meno quelli doppi:

uno.symmetric_difference_update(due) # {'birra', 'pizza', 'Roby',


'Pino'}

- Set congelati sono una categoria a parte. Tra l’altro, Python li considera tipi di
dati nativi. E anche se hanno le stesse qualità degli insiemi, inclusi i metodi di
classe, sono immutabili come le tuple. Per utilizzare un set congelato, eseguiremo
la funzione frozenset() che passerà un iterabile come argomento. E nel caso in cui
passi un altro tipo di dati come un elenco o una stringa, il set congelato lo tratterà
sempre come un iterabile. Ciò vuol dire che il valore sarà decostruito nelle sue
singole parti, e successivamente ridotto a un insieme di unici immutabili:

Lista = [1,1,2,3,4]
Stringa = "Ciao"
frozenList = frozenset(Lista)
frozenString = frozenset(Stringa)
print(frozenList) # frozenset({1, 2, 3, 4})
print(frozenString) # frozenset({'C', 'i', 'a', 'o'})

Come vediamo, gli insiemi congelati non possono essere dichiarati con una
notazione di caratteri, come le parentesi graffe, quadre o le classiche tonde. In
poche parole, per usare un set congelato, dovremo dichiararlo.
Anche se a prima vista sembra che adoperandoli non vi siano dei vantaggi in
termini di prestazioni, sono invece di grande utilità per elaborare un codice più
chiaro e conciso. In poche parole, definendo una variabile come un insieme
congelato, stiamo implicitamente segnalando a chi potrebbe osservare il listato che
questo non è modificabile.

43
Le liste
Sono la struttura dati più basilare in Python corrispondente a una sequenza di
valori differenti, chiamati elementi. Questa tipologia di insieme è racchiusa tra le
parentesi quadre, proprio come le stringhe sono definite da caratteri tra virgolette.
Sono particolarmente utili quando si lavora con molti valori correlati, per tenere
insieme i dati, condensare il codice ed eseguire gli stessi metodi e operazioni su
più valori contemporaneamente. Essenzialmente, le liste sono: mutabili, ordinate,
dinamiche, dispongono di un indice per accedere agli elementi dell'elenco e
possono essere nidificate.
Forse, a questo punto, qualcuno potrebbe chiedersi il motivo di avere così tante
tipologie d’insiemi differenti. La risposta sarebbe quella di pensare alle differenti
strutture di dati configurate come raccolte esistenti nel concreto e che tutti noi
abbiamo a disposizione, come, ad esempio: i download generici del nostro
portatile, i nostri specifici file personali, le playlist di canzoni, la cronologia delle
email o del browser, la raccolta di documenti e immagini a cui possiamo accedere
su un servizio cloud e altro ancora. Ne consegue che, con tali elenchi, una
sequenza ordinata di elementi può diventare anche un unico elemento di questo
insieme, e quindi, potremo chiamarlo individualmente, attraverso l'indicizzazione.
In questo caso, le liste sono un tipo di “dato composto” costituito da parti più
piccole e sono molto flessibili perché consentono di aggiungere, rimuovere e
modificare i valori. In pratica, quando sarà necessario archiviare molti valori o
eseguire iterazioni su di essi, ed essere comunque in grado anche di modificarli,
sarà il caso di affidarsi a questa tipologia di insieme.
La lista è il tipo di dati più versatile disponibile. La cosa importante è che gli
elementi in una lista non devono necessariamente essere dello stesso tipo e
possono essere scritti come un insieme di valori separati da virgole e racchiusi tra
parentesi quadre:

uno = ['Mario', 'birra', 2023]

44
due = [1, 2, 3, 4, 5, 6]

tre = ["a", "b", "c", "d"]

Analogamente agli indici di stringa, gli indici della lista iniziano da zero e possono
essere tagliati, concatenati e modificati. Vediamo adesso come accedere ai valori
al suo interno:

uno = ['Mario', 'birra', 2024]


due = [6, 12, 24, 36, 48, 56]
print("uno[0]: ", uno[0]) # Mario
print("due[1:5]: ", due[1:5]) # [12, 24, 36, 48]

Se vogliamo aggiornare elementi singoli o multipli, ci serviremo del metodo


append(). Per esempio:

uno = ['Mario', 'birra', 2024]


print ("Valore indice 2: ")
print (uno[2])
uno.append(2023)

print ("Nuovo valore indice 3: ")


print (uno[3])

Per rimuovere un elemento della lista abbiamo due possibilità: usare l'istruzione
del, conoscendo esattamente la sua posizione, oppure applicare il metodo
remove() richiamando l’elemento per intero. Per esempio:

uno = ['Mario', 'birra', 2024]


uno.remove(2024)

45
print(uno)

Le liste rispondono agli operatori + e * in modo molto simile alle stringhe, cioè con
la concatenazione e la ripetizione, tranne per il fatto che il risultato ottenuto non
sarà una stringa, ma, ovviamente, una nuova lista.
Python include le seguenti funzioni da utilizzarsi con questa tipologia di insieme, gli
esempi nella tabella sono ripresi dalle due liste di riferimento sottostanti:

uno = ['Mario', 'birra', 'pizza']


due = ['Mario', 'Roby', 'Pino']

Metodo Utilizzo Esempio

append() Aggiunge una lista ad un’altra uno.append(due)


count() Riporta quantità dell’elemento indicato uno.count("Mario")
clear() Rimuove tutti gli elementi nella lista uno.clear()
extend() Estende una lista con una’altra uno.extend(due)
index() Riporta la posizione dell’elemento uno.index("Mario")
insert() Inserisce l’elemento nella posizione uno.insert(2,'..')
len() Riporta la lunghezza della lista len(uno)
list(seq) Converte una tupla in una lista c = list(a)
max() Riporta il valore massimo nella lista tre = max(...)
min() Riporta il valore minimo nella lista tre = min(...)
pop() Rimuove l’elemento dalla posizione uno.pop(2)
remove() Rimuove l’elemento specificato uno.remove(...)
reverse() Inverte l’ordine degli elementi uno.reverse()
sort([fun]) Ordina elementi, più funz [reverse, key] uno = sort()
update() Aggiunge elementi di altri set a.update(b)

46
I dizionari
Sono una raccolta non ordinata di valori di dati, a differenza delle altre sequenze
che contengono solo un singolo valore come elemento, i dizionari includono la
coppia chiave:valore, utili a diverse operazioni. Di contro non consentono il
polimorfismo, cioè quando lo stesso metodo viene dichiarato più volte, per scopi e
classi diverse, lo approfondiremo più avanti nel quinto capitolo.
Un dizionario si crea inserendo una sequenza di elementi all'interno di parentesi
graffe {}, separati da virgole. I valori possono essere di qualsiasi tipo di dati e
possono essere duplicati, mentre le chiavi non possono essere ripetute e
dovrebbero essere immutabili. Inoltre, le chiavi del dizionario fanno distinzione tra
maiuscole e minuscole. Infine, gli stessi nomi ma con chiavi diverse verrebbero
ovviamente trattati distintamente.
Per accedere agli elementi del dizionario, utilizziamo le parentesi quadre insieme
alla chiave per ottenerne il valore. Vediamolo meglio:

uno = {'Nome': 'Mario', 'Età': 55, 'Genere': 'M'}


print ("uno['Nome']: ", uno['Nome'])
print ("uno['Età']: ", uno['Età'])

Un dizionario può essere aggiornato semplicemente aggiungendo una nuova voce


o una coppia chiave-valore, che modificherà o eliminerà una voce esistente come
mostrato nel semplice esempio seguente:

uno = {'Nome': 'Mario', 'Età': 55, 'Genere': 'M'}


print(uno)
uno['Nome'] = 'Roby' # aggiorniamo la voce esistente
uno['Genere'] = 'F' # anche qui
uno['Lavoro'] = 'Avvocato' # inseriamo una nuova voce
print (uno)

47
Anche nei dizionari possiamo rimuovere gli elementi, abbiamo due opzioni:
eliminare un singolo elemento o cancellare l'intero contenuto. L’istruzione del ci
permetterà di eliminare solamente una chiave o tutto il dizionario, in quest’ultimo
caso basterà non esplicitare le parentesi, ad esempio: del uno. Con il metodo
clear(), già visto in precedenza, rimuoveremo tutte le voci del dizionario:

uno = {'Nome': 'Mario', 'Età': 55, 'Genere': 'M'}


del uno['Nome'] # rimuove la chiave Nome
print (uno)
uno.clear() # rimuove tutte le voci nel dizionario
print(uno)

del uno # cancella tutto il dizionario


print(uno) # qui avremo un errore, il dizionario non esiste più

Normalmente, i valori del dizionario non hanno restrizioni: possono essere oggetti
standard od oggetti definiti dall'utente. Tuttavia, lo stesso non vale per le chiavi:
infatti, non è consentito averle duplicate. In tale caso verrebbe considerato solo
l’ultimo elemento indicato, ad esempio un dizionario con: Nome: Mario, Nome:
Roby, otterrebbe come output solamente: Nome: Roby.
Inoltre le chiavi devono essere immutabili, il che significa che possiamo usare
stringhe, numeri o tuple come chiavi del dizionario, ma non dizionari, liste o set
che sono invece mutabili.
Nella tabella di seguito elenchiamo le funzioni incorporate dei dizionari:

Operatore Utilizzo Esempio

clear() Rimuove tutti gli elementi dal dizionario uno.clear()


cmp(d1, d2) Confronta gli elementi dei dizionari cmp(uno, due)

48
copy() Riporta una copia del dizionario uno.copy()
del Elimina un elemento o tutto il dizionario del uno[...]
fromkeys(k, v) Dizionario con chiavi e valori specifici dict.fromkeys(..)
get(k, v) Riporta il valore dell’elemento uno.get(...)
items() Riporta gli elementi del dizionario uno.items()
keys() Riporta le chiavi del dizionario uno.keys()
len() Riporta la lunghezza del dizionario len(uno)
pop() Rimuove la chiave specifica uno.pop('Età')
popitem() Rimuove l’ultimo elemento uno.popitem()
setdefault() Imposta il valore specificato uno.setdefault(.)
str(diz) Riporta il dizionario in stringa str(uno)
update() Inserisce gli elementi specificati uno.update({..})
values() Riporta tutti i valori nel dizionario uno.values()

Le tuple
Sono praticamente identiche alle liste, a eccezione delle seguenti proprietà:
vengono definite racchiudendo gli elementi tra parentesi tonde invece che quadre
e, al contrario delle liste, sono immutabili. Solitamente vengono scelte nei seguenti
casi:

- L'esecuzione del programma è più veloce quando si manipola una tupla rispetto a
una lista.
- Le tuple vengono utilizzate per impedire la modifica dei dati. Se i valori nella
raccolta devono rimanere costanti per tutta la durata del programma, l'utilizzo di
una tupla invece di un elenco protegge il tipo di dati da modifiche accidentali.
- Una tupla può essere utilizzata per le istanze in cui si desidera utilizzare un tipo
di dati del dizionario, che richiede come uno dei suoi componenti un valore di tipo
immutabile.

49
La tupla vuota è rappresentata da due semplici parentesi, inoltre una tupla può
contenente anche un singolo valore, sempre con la virgola:

uno = (2,)

La creazione di una tupla con diversi valori richiede sempre la separazione con le
virgole, mentre le parentesi non sono obbligatorie:

uno = ('Mario', 'birra', 2023)


due = (6, 12, 24, 36, 48, 56)
tre = "a", "b", "c"

Come gli indici di stringa, gli indici di tupla iniziano da zero e successivamente
possono essere tagliati, concatenati e modificati.
Per accedere ai valori in tupla, è possibile utilizzare le parentesi quadre per
affettare insieme all'indice o agli indici per ottenere il valore disponibile in
quell'indice. Per esempio:

uno = ('Mario', 'birra', 2023)


due = (6, 12, 24, 36, 48, 56)
print ("uno[0]: ", uno[0]) # Mario
print ("due[1:5]: ", due[1:5]) # [12, 24, 36, 48]

Come accennato in precedenza, le tuple sono immutabili, il che significa che non è
possibile aggiornare o far avanzare i valori dei suoi elementi. Si possono invece
prendere parti di tuple esistenti per crearne di nuove, come dimostra il seguente
esempio:

tupla1 = (10, 20, 30)


tupla2 = ('abc', 'xyz')
50
tupla1[3] = (100) # Questa riga darà un errore
tupla3 = tupla1 + tupla2

print(tupla3) # (10, 20, 30, 'abc', 'xyz')

Non è consentito aggiungere o rimuovere singoli elementi dalla tupla. Tuttavia,


come abbiamo appena visto, non sembrano esserci problemi nel mettere insieme
due tuple. Mentre, per rimuovere esplicitamente un'intera tupla, useremo
l'istruzione del:

tupla = ('Mario', 'Roby', 2023)


print (tupla)
del tupla

print (tupla) # errore

Le tuple rispondono agli operatori + e * in modo molto simile alle stringhe, infatti,
anche in questo caso hanno il significato di concatenazione e ripetizione, tranne
per il fatto che ovviamente il risultato sarà una nuova tupla, e non una stringa.
Andiamo adesso a mostrare alcune istruzioni che potranno esserci utili con l’uso di
questo tipo di insieme:

Operatore Utilizzo Esempio

count() La quantità dell’elemento indicato uno.count('Mario')


cmp(t1, t2) Confronta gli elementi tra le due tuple cmp(uno, due)
index() Riporta la posizione dell’elemento uno.index(..)
len() Riporta la lunghezza della tupla len(uno)
max() Riporta il valore massimo nella tupla max(uno)

51
min() Riporta il valore minimo nella tupla min(uno)
tuple() Converte una lista in una tupla lista=tuple(..)

In conclusione è utile parlare ancora della tipizzazione dinamica, caratteristica di


molti altri linguaggi di scripting come Perl o PHP. Ci aiuta a scrivere listati più
condensati, ciò significa più tempo utile per scrivere altro codice. Inoltre,
apprendere questo concetto ci servirà anche nella comprensione di una parte del
materiale contenuto nel libro.
Essenzialmente abbiamo imparato che i tipi di dati sono importanti perché in
Python il loro abbinamento o associazione a metodi e funzioni è soggetto a delle
regole. Potrebbe essere necessario analizzare i dati in modo diverso e quindi
classificarli, in caso contrario, risulterebbe un'analisi errata. I tipi di dati sono
essenzialmente necessari per sapere quale tra loro contiene o può contenere una
variabile. Se conosciamo queste informazioni in anticipo, possiamo salvarci da
tanti problemi di runtime e utilizzare uno spazio di memoria minimo.

Le iterazioni
Nella programmazione, oltre ai tipi di dati, abbiamo a disposizione diverse
strutture logiche che ci consentono di gestire il flusso del nostro codice. Queste
strutture sono note come controllo del flusso e ci permettono di eseguire
determinate azioni in modo condizionato o ripetitivo.
Tra queste strutture, i cicli di programmazione ci introducono al concetto di
iterazione. Essenzialmente è il processo di esecuzione ripetuta di un insieme di
istruzioni per un numero di volte solitamente prestabilito, al fine di ottenere un
risultato utile. In pratica, si tratta di un'azione di ripetizione che viene effettuata
finché non si raggiunge una condizione cosiddetta di uscita.
In questo contesto, una sequenza ripetuta di istruzioni è definita come loop, ciclo o
iterazione. È particolarmente utile quando dobbiamo eseguire la stessa operazione
più volte con valori differenti, come ad esempio quando vogliamo analizzare tutti

52
gli elementi di una lista o quando dobbiamo eseguire una serie di calcoli iterativi.
Vediamone un piccolo riassunto, prima con for e poi con while:

for i in range(9):
print("Sono il loop! passaggio n ",i)

Rispetto ad altri linguaggi in Python, possiamo semplicemente utilizzare una


funzione elegante chiamata range per impostare il numero di iterazioni come
appena osservato.
Passiamo a while:

x=0
while x<10:
x=int(input('digita un numero più alto di 10: '))

In poche parole il ciclo while, sussisterà finché l’utente non inserirà un numero
maggiore o pari a dieci. Vediamo un esempio più complesso, ma anche più
divertente:

import random

rn = random.randint(1, 10)
tentativi = 0

print("Sto pensando a un numero da 1 a 10.")

while True:
tentativi += 1
tentativo = int(input("Prova a indovinarlo! "))

53
if tentativo == rn:
print(f"Complimenti! Hai indovinato il numero in {tentativi}
tentativi!")
break
elif tentativo < rn:
print("Il numero che hai inserito è troppo basso. Riprova.")
else:
print("Il numero che hai inserito è troppo alto. Riprova.")

In questo esempio, usiamo un ciclo while per chiedere all'utente d’indovinare un


numero da 1 a 10. Questo verrà memorizzato nella variabile rn, che viene
calcolata utilizzando il modulo random importato all’inizio del listato.
A ogni iterazione del ciclo while, il programma chiede all'utente d’inserire un
numero che poi sarà memorizzato nella variabile tentativo. Successivamente il
programma confronterà il numero inserito dall'utente con la variabile rn.
Se il numero inserito dall'utente sarà uguale al numero da indovinare, il
programma stamperà un messaggio di congratulazioni insieme al numero di
tentativi effettuati e uscirà dal ciclo attraverso l'istruzione break.
Se il numero inserito dall'utente sarà diverso dal numero da indovinare, il
programma fornirà un suggerimento per aiutare l'utente a trovare il numero
giusto. In particolare, se il numero inserito dall'utente è inferiore, il programma
stamperà il messaggio "Il numero che hai inserito è troppo basso. Riprova." e se il
numero inserito dall'utente è superiore, il programma stamperà il messaggio "Il
numero che hai inserito è troppo alto. Riprova.". Il ciclo while continuerà finché
l'utente non indovinerà il numero.
Andiamo avanti, oltre ai più utilizzati for e while abbiano anche il classico loop "do-
while" anche se Python non ha una sintassi nativa per questa tipologia di ciclo, si
può simulare usando un loop while con una condizione post-loop. In questo modo,
il codice all'interno del ciclo verrà eseguito almeno una volta, indipendentemente
dalla condizione, vediamo:
54
i = 0
while True:
print(i)
i += 1
if i == 5:
break

In questo esempio, while è impostato su True, ma viene interrotto quando la


variabile i raggiunge il valore 5. Poiché non c'è alcuna condizione di controllo
all'inizio del loop, il codice all'interno del loop verrà eseguito almeno una volta.
Da tenere in considerazione anche Il ciclo for-else: questa istruzione è simile al
costrutto if-else. Il blocco else viene eseguito solo se il ciclo for completa il suo
compito fino alla fine senza alcuna interruzione. Al contrario del blocco if dove
viene eseguito solo se la condizione è True, vediamo un esempio:

numeri = [1, 2, 3, 4, 5]
for numero in numeri:
if numero == 0:
break
else:
print("Il ciclo for è stato eseguito senza interruzioni.")

In questo esempio, il ciclo for viene eseguito per ogni elemento nella lista numeri,
ma non ci sono elementi uguali a 0. Quindi il blocco else viene eseguito.
Da segnalare in fine il ciclo enumerate: questa istruzione viene utilizzata per
ottenere una coppia di indici-valori per ogni elemento di una sequenza:

numeri = [1, 2, 3, 4, 5]
for i, numero in enumerate(numeri):

55
print(i, numero)

In questo esempio, il loop for si serve della funzione enumerate per iterare
attraverso ogni elemento nella lista numeri e restituire una coppia di indici-valori
per ogni elemento.
Vi sono anche altre istruzioni di loop in Python come break, continue, pass e
return, ma queste in realtà sono indicazioni di controllo del flusso e non
propriamente comandi relativi al ciclo.
Adesso approfondiremo le iterazioni con il supporto delle funzioni. Dato che
l’argomento funzioni è nel terzo capitolo, per una migliore comprensione potete
anche ripassa queste due pagine in un secondo momento.
Oltre alle istruzioni appena viste, sussistono interazioni aggiuntive che potranno
servirci a eseguire iterazioni su degli oggetti specifici tramite le funzioni:

- map(): utilizzato per applicare una funzione su ogni elemento di un insieme


(tupla, lista, set). In pratica map() restituisce un oggetto mappa che potrà anche
essere poi convertito in una lista o in una tupla:

def quadrato(x):
return x**2

numeri = [1, 2, 3, 4, 5]
risultati = map(quadrato, numeri)

print(list(risultati)) # [1, 4, 9, 16, 25]

- filter(): utilizzato per scremare un insieme (lista, tupla, set) basandosi su un


filtro specifico. Perciò filter() restituirà un oggetto filtro, il quale potrà essere
convertito in una tupla o in una lista:

56
def dispari(x):
return x % 2 != 0

numeri = [1, 2, 3, 4, 5]
risultati = filter(dispari, numeri)

print(list(risultati)) # [1, 3, 5]

- reduce(): utilizzato per ridurre un insieme ad un singolo valore, attraverso una


funzione specificata. Per il suo utilizzo è necessario importarla attraverso functools
(https://docs.python.org/3/library/functools.html#module-functools):

from functools import reduce

def somma(x, y):


return x + y

numeri = [1, 2, 3, 4, 5]
risultato = reduce(somma, numeri)

print(risultato) # 15

- enumerate(): utilizzato per iterare su un insieme per restituire una tupla


contenente l’indice dell’elemento e l’elemento stesso:

programmatori = ['Mario', 'Roby', 'Tony', 'Sara']

for i, f in enumerate(programmatori):
print(i, f) # 0 Mario 1 Roby 2 Tony 3 Sara

57
Distinzione tra iterabile e iteratore
Un argomento molto importante, che spesso contribuisce a creare molta
confusione a chi è alle prime armi riguarda la distinzione tra iterabile e iteratore.
Sono due oggetti differenti, anche se in relazione tra loro: un iterabile è un
qualsiasi oggetto che può essere trattato come una sequenza o un insieme (tupla,
lista, dizionario) e per un loop. Mentre un iteratore è figlio dell’iterabile, infatti è
prodotto da un suo metodo. Questo oggetto ci permette di gestire gli elementi
dell’iterabile usando l’istruzione next().
Nello specifico Python supporta un concetto di iterazione sui contenitori e utilizza le
classi definite dall'utente del contenitore per supportare l'iterazione. Le sequenze,
descritte più dettagliatamente di seguito, supportano sempre i metodi di
iterazione:

_ iter__(): Restituisce un oggetto iteratore. Questo processo è necessario per


supportare il protocollo iteratore descritto dopo il prossimo esempio. Se un
contenitore supporta diversi tipi di iterazione, è possibile fornire metodi aggiuntivi
per richiedere specificamente iteratori per quei tipi di iterazione:

class Numero:
def __iter__(x):
x.y = 1
return x
def __next__(x):
z = x.y
x.y += 1
return z
classe = Numero()
it = iter(classe)
a=1
while a<2: print(next(it)), a==a+1 # stampa da 1 a 30

58
Come detto, gli stessi oggetti iteratore devono supportare i due metodi seguenti,
che insieme formeranno il protocollo iteratore:

iteratore.iter(): Restituisce l'oggetto iteratore stesso. Ciò è necessario per


consentire l'utilizzo di contenitori e iteratori con le istruzioni for e in.
iteratore.next(): Restituisce l'articolo successivo dal contenitore. Se non ci sono
altri elementi, solleva l'eccezione StopIteration. In tal caso, il metodo next() dovrà
continuare a farlo nelle chiamate successive. Le implementazioni che non
obbediscono a questa proprietà sono considerate non funzionanti:

lista = ["Mario", 25, 100]


x = iter(lista)

print(next(x)) # prende e visualizza il primo elemento


print(next(x)) # il secondo
print(next(x)) # e infine il terzo

Abbiamo visto diversi oggetti iteratori per supportare l'iterazione su tipi di


sequenze generali e specifiche, liste e altre forme più specializzate. Ricordiamoci
invece che i tipi specifici non sono importanti al di là della loro implementazione
del protocollo iteratore.

Approfondiamo gli operatori


Abbiamo già visto gli operatori utilizzati per il confronto tra due valori, adesso
andremo a elencarne l’intero pacchetto. Gli operatori sono un importante elemento
di scripting in Python, sono utilizzati per eseguire operazioni su variabili e valori.
Sono suddivisi nei seguenti gruppi:

- Operatori di confronto: comparano due valori.


59
- Operatori di assegnazione: attribuiscono un valore alle variabili.
- Operatori aritmetici: servono per eseguire le classiche operazioni matematiche.
- Operatori logici: combinano istruzioni condizionali.
- Operatori d’identità: paragonano le variabili per capire se sono (o non sono) il
medesimo oggetto con identica posizione in memoria.
- Operatori di appartenenza: verificano se una sequenza definita sussiste in un
oggetto.
- Operatori bit a bit: servono a confrontare solamente i numeri binari.

Di seguito una tabella esplicativa di tutti gli operatori appena descritti:

Operatori Utilizzo Esempio


aritmetici
+, -, *, / Le operazioni aritmetiche basiche (a+1)*(b-2)/c
% Operatore modulo, restituisce il resto 5 % 2 = 2 resto 1
// Divisione intera 5 // 2 = 2
** Elevare a potenza 5**2 = 25
Operatori
Utilizzo Esempio
assegnazione
= Uguaglianza a = 2
+=, -=, *=, Scorciatoia per evitare di digitare due a+=2, a-=2, a*=2,
/= volte lo stesso valore, esempio: a=a+2 a/=2
%=, //=, **= Come sopra: a=a**2 a**=2
&= Uguaglianza a due valori: a=a & 2 a&=2
^= Operazione di XOR bit a bit a=2, a^=1 # 3
|= Operazione di OR bit a bit a=2, a|=1 # 3
<<= >>= Shift logico a destra o sinistra a=2, a<<=2 # 8
Operatori
Utilizzo Esempio
logici

60
and Riporta True se la condizione è corretta a=1 and b<5
or Riporta True se una delle condizioni è ok a=1 or b<5
not Riporta False se la condizione è corretta not(a=1 and b<5)
Operatori
Utilizzo Esempio
identità
is True: le variabili sono lo stesso oggetto a is b
is not Riporta true se non lo sono a is not b
Operatori
Utilizzo Esempio
appartenenza
in Riporta True se la sequenza è reale a in b
not in Riporta True se la sequenza è reale a not in b
Operatore Utilizzo Esempio
bit a bit
& Riporta 1 se i tutti bit sono 1, altrimenti 0 x & y
^ Riporta 1 solo se uno dei bit è 1, sennò 0 x ^ y
| Riporta 1 se uno dei bit è 1, sennò 0 x | y
~ Inverte i bit ~x
>> Sposta la sequenza di bit a destra x>>
<< Sosta la sequenza di bit a sinistra x<<

Vediamo un esempio più chiaro riguardante gli operatori che ci permettono di


cambiare il valore di una variabile a livello di bit. Abbiamo già detto che riguardano
solo i numeri binari, vediamo un esempio (che non è codice Python):

x = 8 = 1000 (il numero 8 in codice binario)


y = 7 = 0111
z = 11 = 1011
x & y = 1000 & 0111 = 0000

61
Questo risultato è dato dal fatto che l’operatore & riporta 1 solo se entrambi i
valori di raffronto sono 1, altrimenti ritorna 0, quindi confrontando l’8 e il 7 binari,
cioè 1000 e 0111, noteremo che nessuno dei quattro valori è due volte uno, perciò
il risultato sarà 0000, ossia 0 in decimale. Vediamo altri esempi:

x ^ y = 1000 & 0111 = 1111 (il numero 15 decimale)


x | z = 1000 & 1011 = 0011 (il numero 3 decimale)

I primi passi
Dopo aver appreso alcune basi su Python proviamo a vedere del codice semplice,
anche per applicare tutto ciò che abbiamo visto finora. Partiamo con delle
operazioni con le stringhe:

Sport = 'calcio'
print("Quest'inverno mi impegnerò nel: ", Sport)

Come già visto, notiamo come la virgola separi la variabile dalla stringa e che, a
differenza di altri linguaggi, dopo print ciò che segue deve essere tra parentesi.
Proviamo questo:

Sport = {'ciclismo', 'calcio', 'fitness'}


print("Quest'anno mi allenerò con il :", Sport)

Chi ha un background di conoscenza di altri linguaggi, si accorgerà di come le liste


sono differenti rispetto agli altri insiemi in Python, come appena visto con la
variabile Sport che è stata mostrata interamente, comprese virgolette e parentesi.
Andiamo avanti e osserviamo le modalità per effettuare le operazioni che tutti
quanti conosciamo, sottrazioni, addizioni, divisioni, moltiplicazioni e anche
elevamento a potenza:

62
x = 8+2-2*10/2
print(x) # risultato 0

Vediamo adesso come effettuare prima la somma e la differenza:

x = (8+2-2)*10/2
print(x) # risultato 40

Elevamento a potenza:

print(10**2) # dieci elevato alla seconda: 100

Le operazioni basiche si possono anche eseguire così:

x = 7
y = x + x * 2
print(y) # risultato 21

Oppure:

x = 7
x += 2
print(x) # risultato 9

Proseguendo vedremo come convertire questi dati. Essenzialmente osserveremo le


modalità per cambiare la tipologia di una variabile in Python.

Typecasting
Nel momento in cui iniziamo a scrivere codice più complesso, ci sarà la necessità di
definire specificamente la tipologia di alcune variabili. Ad esempio, ci potrebbe

63
essere l’occorrenza di convertire un numero in virgola mobile in un intero, o
viceversa, per vari motivi, come il computo o la semplice necessità. Questo
processo è noto come typecasting o conversione di tipo. Python ci offre dei metodi
elementari ma potenti per effettuare questa operazione.
Esistono due tipi di conversione: implicita ed esplicita. La prima è già stata vista
negli esempi precedenti. In sostanza, quando Python deduce il tipo di variabile
dall'input che gli viene assegnato, ad esempio con una lista, un set, una stringa o
un numero, sta facendo una conversione implicita.
Per quanto riguarda il typecasting esplicito, invece, il programmatore definisce
esplicitamente la tipologia di dati di una variabile. Ad esempio, potrebbe assegnare
un valore a una variabile e contemporaneamente convertire il suo tipo di dato in
un altro. Di seguito viene presentata una lista delle variabili più comuni e le loro
possibili conversioni:

- int() serve a cambiare le variabili in un numero intero:

anni = "55"
n_var = int(anni)
print(n_var)

- str() fa di un numero intero o in virgola mobile una stringa:

prezzo = float(55.5)
n_var = str(prezzo)
print(n_var)

- set() cambia una stringa in un set:

prezzo ="cinquanta"
n_var = set(prezzo)

64
print(n_var)

- list() cambia una stringa in una lista:

prezzo ="cinquanta"
n_var = list(prezzo)
print(n_var)

- float() cambia in un numero in virgola mobile:

prezzo = 55
n_var = float(prezzo)
print(n_var)

- complex() per convertire x in un numero complesso con parte reale x e parte


immaginaria zero:

reale = 3
immaginario = 4
complesso = complex(reale, immaginario)
print(complesso)

- complex(x, y) per convertire x e y in un numero complesso con parte reale x e


parte immaginaria. x e y sono espressioni numeriche:

parte_reale = 3
parte_immaginaria = 4
numero_complesso = complex(parte_reale, parte_immaginaria)
print(numero_complesso)

65
Oltre a queste conversioni esplicite, in Python abbiamo funzioni incorporate che
eseguono dei calcoli matematici. Queste operazioni le consideriamo sempre una
sorta di conversione, perché, di fatto, da uno o più valori iniziali dati, ne otteniamo
uno o più differenti. In qualche caso, oltre a mutare il valore, cambia anche il tipo,
ad esempio da numero intero a numero in virgola mobile (float). Queste funzioni
sono comunemente usate per videogiochi, ma anche per la sicurezza.
Per servirsi di queste istruzioni, all’inizio del nostro listato dovremo usare import
math. Inoltre, negli esempi mostrati in tabella faremo riferimento a questo
esempio:

a = 10, b = (1,2,3), Il numero di Eulero (E):

Funzione Utilizzo Esempio

abs() Calcola il valore assoluto (intero) abs(a)


ceil() Arrotonda per eccesso all’intero vicino math.ceil(a)
exp(a) Riporta ‘E’ elevato al valore a math.exp(a)
fabs() Calcola il valore assoluto come float math.fabs(a)
floor() Arrotonda un numero all’intero vicino math.floor(a)
log() Calcola il logaritmo di un numero math.log(a)
log10() Logaritmo base-10 di un numero math.log10(a)
prod() Calcola il prodotto da una sequenza math.prod(b)
isqrt() Arrotonda la radice quadrata all’intero math.isqrt(a)
pow(a, b) Eleva il valore a al valore b math.pow(3, 5)
round() Arrotonda un valore round(a)
sqrt() Calcola la radice quadrata del numero math.sqrt(9)

Un’importante branca della matematica riguarda la generazione di numeri casuali,


utilizzati principalmente per applicazioni di giochi, sicurezza e privacy. Includono le

66
funzioni elencate di seguito, ricordandosi sempre che per utilizzarle bisognerà
utilizzare import random all’inizio del listato.

Funzione Utilizzo Esempio

choice() Un elemento casuale da un elenco random.choice(...)


randint() Un numero casuale (int) da un intervallo random.randint(2, 7)
random() Un numero casuale (float) tra 0.0 e 1.0 random.random()
seed() Imposta il generatore di numeri casuali random.seed(20)
shuffle() Mischia un insieme dato casualmente random.shuffle(...)
uniform() Un numero casuale (float) tra 2 numeri random.uniform(2, 7)
sample() Tot elementi casuali da un insieme random.sample(...,t)

Per concludere, la scienza e il calcolo efficaci basati sui dati richiedono la


comprensione di come questi vengono utilizzati e manipolati. Nel capitolo, abbiamo
tentato di delineare e esplicitare al meglio il modo in cui gli insiemi di dati vengono
gestiti in Python. In generale, un tipo di dati viene utilizzato per definire il formato,
impostare i limiti superiore e inferiore dei dati in modo che un programma possa
applicarlo in modo appropriato. Tuttavia, i tipi di dati Python vengono creati per
molto di più. In Python non è necessario dichiarare una variabile senza menzionare
esplicitamente il tipo di dati. Invece, Python determina il tipo di un letterale
direttamente dalla sintassi in fase di esecuzione. Questa funzione è nota anche
come digitazione dinamica.

Utilizzare i moduli
In Python, l'istruzione import viene utilizzata per caricare i moduli esterni nello
script o nel programma corrente. Un modulo è un file contenente definizioni e
istruzioni di Python che possono essere importati e utilizzati in altri programmi. La
sintassi per importare un modulo è la seguente:

67
import <modulo>

modulo, ovviamente, è il nome del modulo che si vuole importare. In seguito sarà
possibile utilizzare le sue funzioni, classi e variabili nel nostro programma. Ad
esempio, per importare il modulo math, si deve scrivere:

import math

Dopo aver importato il modulo math, è possibile utilizzare le sue funzioni come
sqrt() e sin():

import math
print(math.sqrt(4)) # 2.0
print(math.sin(math.pi/2)) # 1.0

Si può anche utilizzare la parola chiave as per dare al modulo un nome diverso
durante l'importazione:

import math as m
print(m.sqrt(4)) # 2.0

Inoltre, è possibile importare specifiche funzioni o variabili da un modulo


utilizzando la parola chiave from:

from math import pi, sqrt

print(sqrt(4)) # 2.0
print(pi) # stampa 3.141592653589793

68
Questa sintassi consente di importare solo le funzioni o le variabili specifiche di cui
si ha bisogno, anziché importare l'intero modulo.
Essenzialmente l'istruzione import è uno strumento potente che consente al
linguaggio Python di estendere facilmente la funzionalità del proprio programma
caricando moduli esterni contenenti codice predefinito.
Di seguito elencheremo alcuni moduli standard che forniscono funzionalità e
strumenti pronti all'uso:

math: fornisce funzioni matematiche avanzate.


datetime: fornisce funzioni per lavorare con date e orari.
os: fornisce funzioni per interagire con il sistema operativo.
sys: fornisce funzioni per interagire con l'interprete Python.
random: fornisce funzioni per la generazione di numeri casuali.
re: fornisce funzioni per lavorare con espressioni regolari.
json: fornisce funzioni per lavorare con il formato di dati JSON.
csv: fornisce funzioni per lavorare con il formato di dati CSV.
socket: fornisce funzioni per la comunicazione di rete.
requests: fornisce funzioni per l'invio di richieste HTTP.
sqlite3: fornisce funzioni per lavorare con il database SQLite.
time: fornisce funzioni per la gestione del tempo.
pickle: fornisce funzioni per la serializzazione e deserializzazione di oggetti Python.
unittest: fornisce un framework per la scrittura di test unitari.
logging: fornisce un framework per la gestione dei log.

Da segnalare che ci sono moltissimi altri moduli standard in Python, ognuno con la
propria area di applicazione. Inoltre, ne esistono numerosi prodotti da terze parti
che forniscono funzionalità specializzate utili a svariati scopi. Per approfondire
riportiamo il link delle pagine ufficiali sull’argomento:
https://docs.python.org/3/tutorial/modules.html

69
https://docs.python.org/3/py-modindex.html

Quiz & Esercizi


1) Completare il listato che visualizza i numeri da 1 a 10:

for...in...(1,...):
print()

2) Dichiarare una variabile x e assegnarle il valore 10. Verificare il tipo di dato di x


utilizzando l’istruzione apposita.
3) Scrivere un programma che sommi i numeri da 1 a 10 (1+2+3+4+5...).
4) Scrivere un programma che distingua se un numero è pari o dispari.
5) Scrivere un listato che calcoli la media dei voti di un esame.
6) Scrivere un listato che verifichi se una lista è ordinata in ordine crescente.
7) Scrivere un programma che assegna il valore 10 ad una variabile x e il valore
20 ad una variabile y, quindi calcola la somma di x e y e la visualizzi a video.
8) Scrivere un programma che calcoli l'area di un quadrato dato il valore del lato.
9) Scrivere un programma che converta i gradi Celsius in gradi Fahrenheit.
Utilizzare una costante per rappresentare il fattore di conversione.
10) Scrivere un programma che calcoli l'area di un rettangolo dati la base e
l'altezza. Assegnare i valori della base e dell’altezza a delle variabili.
11) Scrivere una funzione che prenda in input una lista di numeri interi e
restituisca la somma dei numeri pari. Potete completare la struttura del codice in
basso oppure riscriverlo da zero:

def somma(lista):
totale = ...
for ... in lista:
if ... % 2 == ...:
totale += ...
70
return ...
print(.....([.....]))

12) Scrivere una funzione che prenda in input una stringa e la restituisca con le
lettere in ordine invertito.
13) Scrivere una funzione che prenda in input una lista di stringhe e restituisca
una lista contenente solo quelle che hanno una lunghezza maggiore di 5 caratteri.
14) Scrivere una funzione che prenda in input una lista di numeri interi e che
restituisca quello più grande.
15) Scrivere una funzione che prenda in input una lista di parole e che restituisca
quella più lunga.
16) Scrivere una funzione che prenda in input un numero intero e restituisca il suo
valore come stringa.
17) Scrivere una funzione che prenda in input una stringa rappresentante un
numero intero e restituisca il valore intero corrispondente.
18) Scrivere una funzione che prenda in input una lista di numeri come stringhe e
restituisca una lista di numeri interi.
19) Scrivere una funzione che prenda in input una lista di numeri interi e
restituisca una lista di stringhe che rappresentano i numeri interi.
20) Scrivere una funzione che prenda in input un numero intero e restituisca una
lista contenente le sue cifre come numeri interi.
21) Qual è la differenza tra una costante e una variabile?
22) Qual è la sintassi per dichiarare una variabile?
23) Qual è la sintassi per dichiarare una costante?
24) Qual è il tipo di dato che rappresenta un insieme di valori univoci e immutabili?
25) Qual è il tipo di dato che rappresenta un insieme di valori modificabili e
indicizzati tramite chiavi?
26) Qual è il tipo di dato che rappresenta un valore booleano?
27) Quale costrutto viene utilizzato per iterare su una sequenza di valori?

71
28) Come si può interrompere un ciclo while?
29) Qual è l'operatore di assegnazione in Python?
30) Cosa fa la funzione str() in Python?
N.B. tutte le soluzioni sono a pagina 308

Riassunto
Questo paragrafo ha introdotto una quantità notevole di nozioni, le quali ci
serviranno poi da base per comprendere al meglio i passi successivi. Abbiamo visto
le istruzioni più semplici, come usare gli operatori, la differenza tra i vari tipi di
dati, i diversi insiemi e come Python li sfrutta. Infine, le iterazioni e il typecasting.
Per fare nostre tutte queste informazioni suggerisco di studiare questo capitolo
almeno un paio di volte, oltre a completare tutti gli esercizi, anche quelli due volte,
creando il listato richiesto con un approccio differente rispetto alla prima soluzione.
Tutto ciò porterà il livello di comprensione del resto del libro e di Python in
generale a livelli massimi.

72
73
# 2 - L’espressione in Python

Il concetto di espressione
Lavorare con variabili e dichiarazioni
File e directory
Il calendario

Dopo il corposo primo capitolo, dove abbiamo descritto dettagliatamente le varie


tipologie di dati, e, probabilmente anche messo tanta carne al fuoco, andremo
adesso ad approfondire uno degli elementi di base di ogni linguaggio di
programmazione, cioè l'espressione, necessaria a descrivere una relazione logica o
matematica. Nel capitolo vedremo come le espressioni possono essere anche usate
nelle affermazioni e come gli oggetti possono essere etichettati con dei nomi.
Andiamo nel concreto con un esempio, siamo sul nostro sito di ecommerce
preferito e abbiamo ordinato un prodotto. Come funziona il calcolo del prezzo? La
formula considererà questi elementi: il prezzo del prodotto, la quantità e il costo
della spedizione. Vediamo come esprimerlo in Python se il prezzo è di 50 €, per tre
unità con un costo di spedizione di 10 €:

50*3 + 10

Questa è un’espressione costituita da numeri (interi) e operatori. L'ordine delle


operazioni è determinato dalle regole basiche apprese a scuola, quindi prima
verranno eseguite le operazioni tra parentesi e poi le altre, ma non staremo qui ad
approfondire ulteriormente.
Nel caso di operazioni più complesse, dove il loro ordine è incerto, andremo a
inserire la parentesi:
74
((50*3)+(25*4) + 10)

In Python l'espressione senza parentesi e quella tra parentesi hanno lo stesso


significato.
Negli esempi visti abbiamo utilizzato numeri interi come oggetti su cui eseguire gli
operatori. Tuttavia, le espressioni possono essere utilizzate in senso generale,
mentre gli operatori possono essere eseguiti tra oggetti di vario tipo, come, ad
esempio, con le stringhe (testo), o i valori booleani (True o False). Anche se
determinate tipologie di dati non interagiscono tra di loro. Ad esempio, non si può
sommare una stringa con un numero.
Quando l'operazione è stata eseguita correttamente, l'oggetto risultante avrà
sempre un tipo determinato dall'operazione, dall'oggetto che esegue l'operazione e
dal tipo dell'altro oggetto che partecipa all'operazione.
Quando si confrontano due numeri interi, il nuovo valore creato sarà di tipo
booleano, pertanto, l'esecuzione del confronto, sarà un oggetto True o False, nel
nostro caso True:

50*3 + 10 >= 155 # True

Trattando oggetti di tipo stringa, essi saranno racchiusi tra virgolette singole o
doppie. In questo caso l'operatore di addizione equivale all’associazione tra di esse
e alla creazione di un nuovo oggetto stinga (concatenazione, come visto a pag.
37):

'Mario' + 'lino' # Mariolino

Adesso proviamo qualcosa di più complesso, come un’espressione condizionale.


Essa può descrivere il calcolo secondo cui il risultato dovrebbe essere uno o un
altro valore a seconda di una condizione. Questa espressione è interpretata tra tre

75
oggetti. Il primo oggetto è di qualsiasi tipo, il valore presunto quando la condizione
è soddisfatta. Il secondo oggetto è di tipo booleano, la condizione stessa, dopo la
parola if. L'ultimo oggetto è il valore atteso quando la condizione non è
soddisfatta, situata dopo l’istruzione else. Come ci si aspetterebbe, qualsiasi
oggetto può essere sostituito con un'espressione. Vediamo come combinare
l'espressione condizionale:

'10%' if 35*4+70 >= 200 else 'none'

Il compito risolto dall'esempio è il seguente: se l'importo dell'ordine è maggiore di


250 Euro, verrebbe creata una stringa contenente il tasso di sconto con i valori
10%, altrimenti non vi sarà altra offerta.
Sappiamo già che in Python si distinguono tre tipi di numeri: interi, float e
complessi. Come abbiamo accennato in precedenza Il tipo float rappresenta un
decimale e può memorizzare una frazione decimale.
Osserviamo un'espressione in cui vengono sommate due frazioni decimali con lo
stesso valore, dove il primo oggetto numero è stato scritto nel classico formato
come 0.1, mentre l'altro, 10 elevato alla -2, è sempre 0.1:

0.1 == 10e-2 # True

In alcuni calcoli matematici particolari potremmo usare i numeri complessi. Il


modo più semplice per considerare un numero complesso è una coppia di frazioni
decimali in cui la prima è il valore reale e la seconda è quella immaginaria. Il
valore immaginario corrisponde alla radice quadrata di -1:

(0+2j)**2 == -4 # True

Oltre a queste equivalenze, è possibile inserire i confronti in una catena, cioè:

76
y < x and x < 5

La quale può essere meglio rappresentata con:

y < x < 5

Le due espressioni sono equivalenti, salvo che, nel secondo caso, l'espressione sia
molto più snella ed elegante. Questa notazione può essere utilizzata anche tra più
di due confronti.

I nomi delle variabili


Ora che abbiamo visto come eseguire il calcolo in casi specifici, vediamo come
generalizzare un calcolo. In questo caso, gli oggetti vengono assegnati a nomi di
variabili, pensiamole più semplicemente come fossero delle etichette. Di solito
iniziano con una lettera e continuano con lettere o cifre. Abbiamo già visto
l’espressione: '10%' if 35*4+70 >= 200 else 'none'
Adesso vedremo la stessa assegnando i valori a delle variabili. Il vantaggio di
questo approccio è che nel caso in cui dovessimo calcolare l'espressione per altri
numeri, cambieremo semplicemente solo le istruzioni di assegnazione nelle prime
tre righe e non dovremmo modificare le espressioni:

prezzo = 35
unità = 4
SOGLIA = 200
totale = prezzo * unità
applicabile = totale >= SOGLIA
sconto = '10%' if applicabile else 'none'
print(totale, sconto)

77
Per esprimere l'intento che i nomi delle variabili a cui il valore una volta assegnato
non richiedano modifiche, i nomi delle variabili sono scritti in maiuscolo, come la
SOGLIA da superare per ottenere lo sconto appena vista. Questa notazione non è
altro che una convenzione e il linguaggio Python non ne impedisce la modifica.
L'espressione risultante è quindi molto più facile da capire.
È importante sottolineare che lavoriamo con un linguaggio orientato agli oggetti,
perciò non diremo: la formula assegnata al nome della variabile, ma: un oggetto
creato al momento del calcolo dell'espressione.
I nomi delle variabili vengono utilizzati anche per suddividere espressioni
complicate in espressioni più semplici. Ciò può essere ottenuto assegnando una
parte dell'espressione ai nomi delle variabili e quindi utilizzando i nomi delle
variabili al posto delle espressioni estratte. Inoltre, se i nomi delle variabili
assegnati alle espressioni particolari sono scelti bene, il significato dell'espressione
sarà più esplicito.
L’ultimo listato è composto da più righe. Ognuna di esse è un'istruzione, più
precisamente un'istruzione di assegnazione. I programmi consistono in istruzioni
che vengono eseguite una dopo l'altra, esercitando così un certo effetto sul loro
ambiente. L'impostazione predefinita in Python è che in una riga ci sia
un'istruzione.
Le dichiarazioni di variabile da sottoporre a espressioni ovviamente dovranno
essere poste prima di esse, altrimenti l’espressione non riuscirà a produrre
risultati. Se, ad esempio, venisse spostata alla fine del listato la variabile unità, si
verificherebbe un errore nell’espressione prezzo * unità.
L'istruzione di assegnazione è costituita da un nome, un operatore di assegnazione
e un'espressione. In Python, il segno di uguale significa che il risultato
dell'espressione di destra è assegnato al nome della variabile di sinistra, mentre il
doppio segno di uguale (==) viene utilizzato per verificare l'uguaglianza di due
espressioni. È molto importante ricordarsi che l'istruzione di assegnazione non è
un'espressione, perciò non restituirà alcun valore.

78
I nomi delle variabili potranno anche essere cancellati dopo la loro dichiarazione:

sconto_vip = '30%' if applicabile else 'none'


del sconto_vip

Dopo aver eliminato la variabile, un’eventuale sua chiamata genererà ovviamente


un errore, mentre gli oggetti assegnati ai nomi delle variabili conseguentemente
non sono più allocati o referenziati, cioè non più assegnati ad alcun nome di
variabile.

Specificare il tipo
Sappiamo già che Python è un linguaggio tipizzato dinamicamente, cioè che il tipo
di un nome di variabile dipende esclusivamente dal tipo dell'oggetto assegnato. Ciò
significa che attualmente Python non controlla se queste annotazioni di tipo sono
corrette. Agli albori, in Python non era possibile aggiungere esplicitamente
informazioni sul tipo, in poche parole non si poteva esprimere il tipo previsto di
oggetto da assegnare al nome della variabile. Poiché Python viene utilizzato anche
per programmi grandi e complessi, queste annotazioni possono essere molto utili
per gli sviluppatori che magari si servono di qualche tool esterno.
Vediamo come il tipo è specificato dopo il nome della variabile, separato da due
punti:

prezzo: int = 35
unità: int = 4
SOGLIA: int = 200
totale: int = prezzo * unità
applicabile: bool = totale >= SOGLIA
sconto: str = '10%' if applicabile else'nessuno'
print(totale, sconto)

79
Oltre a questo, è fondamentale per la leggibilità del programma inserire degli spazi
in determinati punti delle espressioni e delle affermazioni. La documentazione di
Python contiene una raccomandazione per il posizionamento degli spazi per
garantire una migliore leggibilità: per prima cosa si consiglia d’inserire uno spazio
su entrambi i lati di un segno uguale, non si devono inserire spazi extra per le
parentesi e infine, per impostazione predefinita, gli spazi vengono inseriti attorno
agli operatori. Un'eccezione è se l'ordine delle operazioni nell'espressione è diverso
da sinistra a destra, poiché ci sono parentesi, o alcune operazioni dovrebbero
essere eseguite prima. In questo caso vengono tolti gli spazi, ad esempio:

prezzo*unità + totale/sconto

Un'espressione può anche essere inclusa nei letterali di stringa formattati, detti f-
strings, oppure con valori letterali di stringa formattati con identificatori di
formato:

prezzo: int = 35
unità: int = 4
totale = f'{prezzo} moltiplicato per {unità} = {prezzo*unità}'
print(totale)
print(f'{prezzo:.2f}, {unità:02d}')

Nei casi in cui c'è la lettera f prima delle virgolette di apertura della stringa e le
espressioni sono all'interno delle virgolette, tra le parentesi graffe, il risultato di
queste espressioni verrà inserito dopo che saranno convertite in una stringa.
Nelle stringhe gli identificatori di formato possono anche essere aggiunti dopo
l'espressione, divisi dai due punti, come abbiamo appena visto nell’esempio dove il
formato della variabile prezzo è espresso come frazione decimale con due cifre
decimali, mentre la variabile unità viene visualizzata come numero intero con
avanti lo zero, in questo caso uno, o anche più modificando il valore 02d.
80
Gli identificatori
In poche parole sono i nomi che possono essere dati agli oggetti, alle variabili o
alle funzioni. Esiste una convenzione standard a cui fare riferimento quando
dobbiamo deciderne l’assegnazione. Essenzialmente, quanto dovremo dichiarare
una funzione o una variabile utilizzeremo il carattere minuscolo, al contrario delle
costanti che saranno tutte maiuscole. I seguenti caratteri possono essere presenti
in un nome: iniziare con una lettera o un trattino basso e proseguire con un
carattere simile a una lettera, un trattino basso o una cifra. Simile a una lettera
significa che altre categorie vengono aggiunte ai caratteri considerati lettere nello
standard Unicode, vale a dire il "segno di non spaziatura", il "segno di
combinazione della spaziatura" e la "punteggiatura del connettore". All'interno dei
nomi, Python distingue lettere minuscole e maiuscole, ma alcune combinazioni di
caratteri possono essere considerate uguali secondo lo standard Unicode. Per gli
identificatori è sconsigliato l’utilizzo di caratteri cirillici, arabi o di ideogrammi:

# Dichiarazione di una variabile con un identificatore valido


nome_studente = "Marco"

# Dichiarazione di una variabile con un identificatore valido


voto_mate = 8.5

# Dichiarazione di una funzione con un identificatore valido


def calcola_media(voti):
somma = 0
for voto in voti:
somma += voto
media = somma / len(voti)
return media

# Dichiarazione di una classe con un identificatore valido

81
class Persona:
def __init__(self, nome, cognome):
self.nome = nome
self.cognome = cognome
def nome_completo(self):
return self.nome + " " + self.cognome

Letterali ed espressioni
I letterali sono oggetti che rappresentano valori di un determinato tipo di dato.
Dell’argomento abbiamo già parlato all’inizio del primo capitolo, ma andiamo ora
ad approfondire alcuni aspetti, nello specifico, le regole per la loro corretta
rappresentazione:
- Il tipo bool ha due valori possibili: True e False.
- Il tipo int rappresenta numeri interi. Un numero intero decimale semplice deve
iniziare con una cifra diversa da zero, ma può continuare con qualsiasi cifra. Se
viene scritto uno zero, possono seguire altri zeri, ma nessun'altra cifra. Se un
numero intero è scritto in un sistema numerico diverso dal decimale, le lettere "b",
"o" o "x" dopo uno zero indicano rispettivamente: il sistema numerico binario,
ottale o esadecimale. Ad esempio, il numero binario 1010 può essere scritto come
0b1010. È possibile utilizzare l'underscore per separare le cifre di un numero in
modo leggibile. Ecco alcuni esempi del numero intero 10 in diversi sistemi
numerici:

10 (decimale) - 1010 (binario) - 101 (ternario) - 012 (ottale) - A (esadecimale)

- Il tipo float rappresenta numeri in virgola mobile. Un punto può essere posto
all'interno o intorno al numero oppure l'esponente del numero 10 moltiplicando il
numero particolare può essere scritto dopo il numero, separato dalla lettera e. I
numeri in virgola mobile in Python seguono lo standard IEEE 754.
- Il tipo complex rappresenta numeri complessi e può essere scritto come due
82
numeri float separati da una lettera j. Ad esempio, il numero complesso 1+√(-1)
può essere scritto come 1j.
- Il tipo str rappresenta una stringa di caratteri. Una stringa può essere scritta tra
virgolette singole o doppie. Se la stringa stessa contiene un tipo di virgolette, ad
esempio quella singola, ovviamente utilizzeremo le doppie per racchiudere la
stringa, e viceversa. Inoltre, se si desidera scrivere una stringa su più righe, è
possibile utilizzare anche le triple virgolette.
Alcuni caratteri speciali possono essere scritti utilizzando sequenze di escape,
come mostrato di seguito:

\b Backspace ASCII (BS). \f Ritorno a capo ASCII (FF).


\n Avanzamento riga ASCII (LF). \r Ritorno a capo ASCII (CR).
\t Tabulazione orizz. ASCII (TAB). \v Tabulazione verticale ASCII (VT).

stringa = "Questo è un esempio di sequenze di escape:\n\t- Backspace:


uno\b due\n\t- Ritorno a capo: tre\f quattro\n\t- Avanzamento riga:
cinque\n\t- Ritorno a capo: sei\rsette\n\t- Tabulazione orizzontale:
otto\t\t\n\t- Tabulazione verticale: nove\v dieci"

print(stringa) # Potrete osservare il risultato nell’output.

Inoltre, potremo servirci della lettera r posizionandola prima della stringa per
interpretare il backslash come un carattere normale, ciò sarà utile per la gestione
dei percorsi di file (c:\miadir\abc) e delle espressioni regolari.
Mentre la lettera f può essere utilizzata per creare una stringa formattata con
valori variabili all'interno della stringa stessa. Infine potremo usare la lettera b per
creare un oggetto di tipo byte.
Adesso osserviamo un esempio per chiarire al meglio alcuni argomenti trattati
finora:

83
# Dichiarazione di una variabile utilizzando un identificatore
nome = "Alice"
# Utilizzo di parole chiave come "if", "else" e "print"
if nome == "Alice":
print("Ciao Alice!")
else:
print("Non sei Alice.")
# Utilizzo di letterali come una stringa e un numero
anni = 25
messaggio = f"Hai {anni} anni"
print(messaggio)
# Definizione di una funzione con un nome e due argomenti
def saluta(nome, saluto):
print(f"{saluto} {nome}!")
# Chiamata della funzione utilizzando argomenti posizionali
saluta("Alice", "Ciao")
# Chiamata della funzione utilizzando argomenti con nome
saluta(saluto="Salve", nome="Bob")

In questo esempio, abbiamo:


- Dichiarato una variabile nome utilizzando un identificatore.
- Utilizzato parole chiave come if, else e print.
- Utilizzato letterali come una stringa Alice e un numero 25.
- Definito una funzione saluta con un nome e due argomenti.
- Chiamato la funzione saluta sia utilizzando argomenti posizionali che con nome.
Passiamo a un ultimo esempio riassuntivo con le diverse espressioni possibili:

# espressione di assegnamento
numero = 10
stringa = "ciao"
# espressione di chiamata di funzione
84
def somma(a, b):
return a + b
risultato = somma(3, 4)

# espressione di operatore
risultato = 5 + 3 * 2 – 1

# espressione di accesso all'attributo


class Persona:
def __init__(self, nome, eta):
self.nome = nome
self.eta = eta
p = Persona("Mario", 30)
nome_persona = p.nome

# espressione di slicing
lista_numeri = [1, 2, 3, 4, 5]
sotto_lista = lista_numeri[2:4]

# espressione condizionali
if numero > 5:
print("Il numero è maggiore di 5")
else:
print("Il numero è minore o uguale a 5")

# espressione di creazione di oggetti


class Animale:
def __init__(self, nome, specie):
self.nome = nome
self.specie = specie
gatto = Animale("Fuffi", "felino")

85
Abbiamo utilizzato l'espressione di assegnamento per assegnare un valore a una
variabile (numero e stringa), l'espressione di chiamata di funzione per chiamare la
funzione somma con due argomenti, l'espressione di operatore per eseguire
un'operazione matematica (5 + 3 * 2 - 1), l'espressione di accesso all'attributo per
accedere all'attributo nome di un oggetto Persona, l'espressione di slicing per
estrarre una parte di una lista, l'espressione condizionale per eseguire il codice in
base a una condizione, e l'espressione di creazione di oggetti per creare un nuovo
oggetto di tipo Animale.

La gestione delle eccezioni


In Python, le eccezioni sono riferite a errori che potrebbero verificarsi durante
l'esecuzione del nostro programma. Errori causati da una vasta gamma di fattori,
come ad esempio l'input dell'utente errato, problemi di connessione a un database,
un file mancante, e tanto altro ancora.
Per gestire la situazione, nello specifico gli errori, Python fornisce un costrutto di
controllo del flusso chiamato blocco try-except. Lo utilizzeremo per eseguire il
codice in modo sicuro, ovvero prevenendo l'interruzione del programma in caso di
errore. In quale modo? Essenzialmente il blocco try-except funziona così: il codice
che potrebbe causare un errore viene posto nel blocco try, mentre il codice per
gestire l'errore viene posto nel blocco except. Se si verifica un'eccezione durante
l'esecuzione del blocco try, il programma salta immediatamente al blocco except e
gestisce l'errore in modo opportuno, senza interrompere il programma.
Grazie a questo costrutto, il programmatore può scrivere codice più robusto e
impermeabile agli errori, che non si interrompe o si blocca completamente in caso
di problemi. Inoltre, la gestione delle eccezioni può anche fornire informazioni utili
all'utente su cosa è andato storto, contribuendo a rendere il programma più
gestibile.
Oltre al blocco try avremo anche Il blocco else che servirà per eseguire codice
quando non ci sono errori, e l’istruzione finally, la quale eseguirà il codice,

86
indipendentemente dal risultato dei blocchi try o else.
Per lavorare con tutte queste istruzioni dovremo costruire delle semplici
espressioni. Vediamole subito in un esempio, dove try genererà un'eccezione, dato
che x risulta indefinito:

try:
print(x)
except:
print("Ecco il nostro primo bug!")

Nello specifico, il blocco try ha generato un errore, perché la variabile x non è stata
definita a monte, quindi sarà eseguito il blocco di eccezione. Senza il blocco try, il
programma si arresterà in modo anomalo e genererà un errore.
Nel caso il listato lo richiedesse si potranno creare vari blocchi di eccezioni per lo
stesso errore, o addirittura crearne uno specifico, ad esempio, relativo a un
modulo inesistente o non installato correttamente:

try:
import moduloinesistente
except ImportError:
print("Il modulo non esiste!")
except:
print("Problema generico")

Qui di seguito invece vediamo che tutto procede senza che vi siano problemi:

try:
print("Saluti dal tuo computer!")
except:
print("Possibile bug!")

87
else:
print("Test passato!")

Vediamo ora un esempio con finally, il quale farà proseguire il programma


nonostante l’errore:

try:
print(x)
except NameError:
print("La variabile non è stata definita!")
finally:
print("Nonostante vi sia un errore il codice andrà avanti.")

Proviamo un esempio leggermente più complesso con tutto ciò che abbiamo visto
finora e anche con qualcosa di nuovo:

import math

while True:
try:
numero = float(input("Inserisci un numero positivo: "))
radice_quadrata = math.sqrt(numero)
except ValueError:
print("Devi inserire un numero positivo!")
except Exception as e:
print(f"Si è verificato un errore: {e}")
else:
print(f"La radice quadrata di {numero} è {radice_quadrata}.")
break
finally:
print("Programma terminato!")
88
Partiamo utilizzando la funzione math.sqrt() per calcolare la radice quadrata del
numero inserito dall'utente. Se viene digitato un numero negativo, otterremo
un'eccezione ValueError e verrà stampato un messaggio di errore.
Se il blocco try non genera un'eccezione, verrà eseguito il blocco else e stampato il
valore della radice quadrata calcolata. Il programma terminerà poi con l'istruzione
break e il blocco finally, il quale verrà sempre eseguito con il suo messaggio.
La novità è il blocco except Exception as e, il quale viene utilizzato per gestire tutte
le altre eccezioni che possono essere generate durante l'esecuzione del
programma e che non sono state gestite esplicitamente. Nello specifico, tutte
quelle che ereditano dalla classe di base Exception.
Il blocco as e cattura l'oggetto eccezione generato e lo assegna a una variabile e,
in modo da stampare un messaggio di errore distinto per quella eccezione.
È importante segnalare che l'uso del blocco except Exception può essere fonte di
problemi perché si rischia di nascondere potenziali errori e comportamenti anomali
del programma. Infatti, generalmente è meglio gestire solo le eccezioni specifiche
di cui siamo a conoscenza e che possono essere previste.
Infine, parliamo di un’ulteriore possibilità di gestione dell’errore attraverso la
parola chiave raise, con la quale potremo scegliere di generare un'eccezione se
dovesse verificarsi una condizione particolare:

import math

def radice_quadrata(numero):
if numero < 0:
raise ValueError("Il numero deve essere positivo!")
return math.sqrt(numero)
numero = float(input("Inserisci un numero positivo: "))
try:
radice = radice_quadrata(numero)
89
print(f"La radice quadrata di {numero} è {radice}")
except ValueError as e:
print(e)

La funzione radice_quadrata controlla se il numero passato alla funzione è


negativo. In tal caso genera un'eccezione ValueError utilizzando l'istruzione raise e
restituisce un messaggio di errore specifico. Altrimenti, la funzione restituisce la
radice quadrata del numero utilizzando math.sqrt().

Abbiamo visto che possiamo gestire l’eccezione in maniera più specifica


selezionando l’eventuale problematica. Di seguito un elenco con quelle più comuni:

- AttributeError: eccezione generata quando si cerca di accedere a un attributo che


non esiste in un oggetto.
- AssertionError: generata quando un'asserzione non è vera.
- FileNotFoundError: generata quando si cerca di aprire un file che non esiste.
- IndexError: viene generata quando si cerca di accedere ad un indice che non
esiste in una sequenza.
- ImportError: viene generata quando si cerca d’importare un modulo che non
esiste o non è installato correttamente.
- IndexError: viene generata quando si cerca di accedere a un indice che non
esiste in una sequenza.
- KeyError: generata quando si cerca di accedere a una chiave inesistente in un
dizionario.
- NameError: viene generata quando si cerca di utilizzare una variabile che non è
stata prima definita.
- OverflowError: viene generata quando il risultato di un'operazione numerica è
troppo grande per essere rappresentato dal tipo di dati utilizzato.
- TypeError: viene generata quando si cerca di effettuare un'operazione su un

90
oggetto di un tipo non corretto.
- UnicodeError: viene generata quando si verifica un errore nella codifica o
decodifica di una stringa.
- ValueError: viene generata quando si cerca di effettuare un'operazione su un
oggetto di un tipo corretto, ma con un valore non valido.
- ZeroDivisionError: generata quando si cerca di dividere per zero.

La gestione di file e directory


Alcune delle funzionalità basilari utilizzate in moltissime applicazioni, e ovviamente
fornite dalla libreria standard di Python, riguardano la gestione dei file. Queste
operazioni, come la manipolazione di un contenuto di un file o la ricerca di una
stringa specifica al suo interno, sono gestite da delle semplici espressioni.
Per aprire un file in Python, avremo a disposizione la funzione open(), la quale
prende come primo argomento il percorso del file e come secondo la modalità di
apertura, che può essere una tra le seguenti:

- a (append): apre un file in cui potremo aggiungere una stringa. Il file sarà in
modalità scrittura e se non esistesse ne sarà creato uno nuovo.
- a+: apre un file sia per lettura che per l’aggiunta. Sarà creato se non esiste.
- ab: apre un file binario per l'aggiunta. Sarà creato se non esiste.
- ab+: apre un file sia per l'aggiunta sia per la lettura in modalità binaria. Se il file
non esiste, ne sarà creato uno.
- b (binary): apre un file in modalità binaria, es. per file immagini o audio.
- r (read): apre un file in modalità di sola lettura, quindi senza poterci scrivere
sopra. Questa è la modalità di accesso predefinita.
- r+: apre un file in modalità lettura/scrittura.
- rb: apre un file di sola lettura in modalità binaria.
- rb+: apre un file in lettura/scrittura in modalità binaria.
- t (text): apre un file in modalità testo.

91
- x (exclusive): apre il file in modalità esclusiva, creando il file se non esiste e
generando un errore se il file già esiste.
- w (write): apre un file in modalità di sola scrittura, lo sovrascrive se esiste già.
- w+: apre un file in modalità lettura/scrittura, lo sovrascrive se esiste già.
- wb: apre un file in modalità binaria di sola scrittura, lo sovrascrive se esiste già.
- wb+: apre un file in modalità lettura/scrittura binaria, lo sovrascrive se esiste.

Ricordiamoci sempre che quando avremo finito le nostre operazioni sui file
dovremo chiuderli attraverso il metodo close().
Infine potremo decidere se utilizzare o no il buffering nel contesto delle operazioni
sui file. Questo meccanismo riguarda una porzione di un file ed essenzialmente si
riferisce al processo della sua memorizzazione in un'area di memoria temporanea
fino a quando non sarà caricato completamente. Il valore zero disattiverà il
buffering, mentre uno lo abiliterà. Ad esempio: nome = open(“prova.txt”, “r”, 1).
Se non è fornito alcun valore, Python utilizzerà l'impostazione predefinita del
sistema che non prevede l’utilizzo del buffer. Solitamente è una buona idea
mantenerlo attivo per aumentare la velocità nelle operazioni sui file.
In Python abbiamo anche tre attributi principali che ci serviranno per lavorare con i
file:

- .closed restituisce True se il file è chiuso, cioè se non si può leggere o scriverci
sopra.
- .mode restituisce la modalità di accesso al file: print(esempio.mode)
- .name restituisce un filename: print(esempio.name)

Ad esempio, per aprire un file di testo in modalità lettura si utilizza la funzione


open(): nome del file oggetto = open("nome del file di testo", access_mode,
buffering). Gli ultimi due attributi sono facoltativi. Vediamo subito degli esempi più
concreti:

92
f = open("esempio.txt", "r")

open("directory/esempio.txt")
prova = open("esempio.txt")

prova = open("dir/esempio.txt", "x = 1")


print(prova.mode) # Output: "x = 1"

prova = open("dir/esempio.txt")
print(prova.read()) # contenuto del file esempio.txt

Per leggere il contenuto di un file, si utilizza il metodo read() dell'oggetto file, che
restituisce una stringa contenente l'intero contenuto del file. Ad esempio:

leggi = open("esempio.txt", "r")


contenuto = leggi.read()

Il metodo read() può anche prendere un argomento che indica il numero di byte
da leggere dal file. Ad esempio, per leggere i primi 10 byte del file si può utilizzare
la seguente istruzione:

leggi = open("esempio.txt", "r")


primi_10_byte = leggi.read(10)

Per scrivere sul file, si utilizza il metodo write() dell'oggetto file, che prende come
argomento la stringa da scrivere sul file. Ad esempio:

scrivi = open("esempio.txt", "w")

93
scrivi.write("Ciao da Mario!")

Al termine dell'utilizzo del file, è importante chiuderlo utilizzando il metodo close()


dell'oggetto file:

prova = open("esmpio.txt", "r")


contenuto = prova.read()
prova.close()

Per leggere il contenuto del file riga per riga, si utilizza il metodo readline()
dell'oggetto file:

f = open("percorso_del_file.txt", "r")
while True:
riga = f.readline()
if not riga:
break
print(riga)
f.close()

Ricordiamo che in Python un file deve restare aperto (open) se volgiamo lavorarci
su. Se fosse chiuso (closed) non potrebbe essere modificato e non potremo
neanche esaminare i suoi attributi. Vediamo in un semplice esempio come funziona
la procedura:

import time

esempio = open("prova.txt", "wb")


print("Nome del file:", esempio.name)
print("Modalità di apertura: ", esempio.mode)

94
print("Controlliamo se il file è chiuso", esempio.closed)
time.sleep(1)
print("Chiudiamo il file di prova")
esempio.close()
print("Il nostro file di prova adesso è chiuso")
time.sleep(1.5)

# controlliamo se il file è stato chiuso prima di accedervi


with open("prova.txt", "r") as f:
print("Controlliamo se il file è stato chiuso", f.closed)

Ci siamo serviti del modulo time solamente per inserire delle pause tra le varie
operazioni.

La gestione delle directory è un aspetto fondamentale in ogni sistema operativo, e


Python offre numerosi modi per manipolare tali elementi. Per eseguire operazioni
sulle directory, è necessario importare il modulo os. Il seguente codice mostra
alcune delle operazioni più comuni:

import os

# Visualizzazione della directory corrente


print("Cartella corrente:", os.getcwd())

# Elenco dei file nella directory corrente


print("Vediamo i file nella cartella:", os.listdir())

# Cambio della directory corrente


os.chdir('C:\\')
print("Cambiamo il percorso della directory:", os.getcwd())

95
# Creazione di una nuova directory
os.mkdir('Prova')
print("Ecco l’elenco dei file in Prova:", os.listdir('Prova'))

# Ridenominazione di una directory


os.rename("Prova", "Altronome")

# Rimozione di una directory vuota


os.rmdir("Altronome")

Con getcwd() abbiamo visualizzato il percorso della directory corrente, mentre con
listdir() elenchiamo i file presenti al suo interno. Per creare una nuova directory, ci
serviremo di mkdir(). Se desideriamo rinominare una directory, è sufficiente
utilizzare os.rename("Prova", "Altronome"). Infine, per rimuovere una directory
vuota, possiamo utilizzare os.rmdir("Altronome").
Se avessimo bisogno di trovare dei file con delle corrispondenze particolari, come
ad esempio un certo nome, potremo sfruttare la corrispondenza al modello dei
nomi dei file. Immaginiamo di avere una cartella piena di meme e di voler trovare
tutti quelli che finiscono con la parola gatto. In questo caso, possiamo utilizzare la
funzione endswith() per controllare se il nome del file termina con gatto:

import os
directory = "/path/to/memes"
for filename in os.listdir(directory):
if filename.endswith("gatto"):
print(filename)

Magari in un secondo momento i nostri amici ci inviano meme con nomi di file
strani e non sappiamo esattamente come cercarli. In questo caso, potremo
utilizzare la funzione fnmatch() per cercare tutti i file che corrispondono a un certo

96
pattern. Ad esempio, per cercare tutti i meme che hanno la parola gatto nel nome,
useremo il seguente codice:

import os
import fnmatch
directory = "/path/to/memes"
for filename in os.listdir(directory):
if fnmatch.fnmatch(filename, "*gatto*"):
print(filename)

In pratica, fnmatch() cerca tutti i file che contengono la parola gatto in qualsiasi
parte del nome del file, utilizzando il carattere jolly * come segnaposto per le parti
del nome del file che non sono importanti o non conosciamo, ad esempio *.doc per
qualunque documento in formato DOC.
Per effettuare delle ricerche più complesse potremo adoperare l’estensione glob e
la relativa istruzione (sempre glob). Il termine globbing si riferisce all'esecuzione di
ricerche di file molto specifiche all'interno di una directory indicata. Il termine glob,
cioè nient’altro che l’abbreviazione di global (globale), ha le sue origini nel mondo
dei sistemi operativi basati su Unix. Vediamo nel prossimo esempio un caso studio
su come sfruttare questa tecnica:

import pandas as pd
import glob

# L’utente indicherà la cartella da controllare


path = 'input'
files = glob.glob(path + '/*.csv')

# Si crea una lista vuota dove andranno i dati dei file CSV
lista = []

97
# Questo è un loop per acquisire i dati della lista
for file in files:
df = pd.read_csv(file)
# Aggiungiamo una colonna con il nome del file
df['File'] = file
lista.append(df)
print(f"Lista di {file} creata con {df.shape[0]} righe")

# Qui uniamo i dati


dataframe = pd.concat(lista, axis=0)
print(f"Dataframe creato con {dataframe.shape[0]} righe")

# Visualizziamo i dati in base al prezzo


dataframe = dataframe.sort_values(by='Prezzo', ascending=False)
print(dataframe[['Prodotti', 'Prezzo', 'File']].head())

Come visto ci serviamo del modulo Pandas per l’analisi dei dati e la funzione glob()
per cercare tutti i file CSV nella directory specificata, e utilizziamo un ciclo for per
leggere e memorizzare i dati di ogni file in una lista. Inoltre, aggiungiamo una
colonna al DataFrame (una struttura derivata da Pandas per organizzare i dati),
per indicare il nome del file di origine, poi uniamo tutti i dati della lista in un unico
DataFrame e li ordiniamo in base al prezzo dei prodotti. Infine, visualizziamo i
prodotti (con nome del prodotto, prezzo e nome del file di origine) utilizzando la
funzione head().

La gestione del tempo


La gestione del tempo in Python è affidata al modulo datetime, il quale è una
libreria integrata che permette di lavorare con le date e gli orari in modo semplice
ed efficiente. È particolarmente utile per le applicazioni che richiedono
l'elaborazione di date e orari, come ad esempio tutte quelle dedicate alla finanza, i

98
sistemi di prenotazione, e i programmi di pianificazione.
Il modulo dispone delle classi date, time, datetime, timedelta, tzinfo e timezone,
che consentono di eseguire operazioni comuni su date e orari, come l'aggiunta di
giorni, la sottrazione di ore, la comparazione di date e così via.
La classe date rappresenta una data, con attributi come anno, mese e giorno,
mentre la classe time rappresenta un orario, con attributi come ora, minuto e
secondo. La classe datetime combina le funzionalità delle classi date e time,
rappresentando una data e un orario insieme.
La classe timedelta rappresenta un intervallo di tempo, come ad esempio la
differenza tra due date, e la classe tzinfo rappresenta il fuso orario, mentre la
classe timezone consente di creare un fuso orario personalizzato.
Il modulo datetime consente anche di formattare le date e gli orari in differenti
formati, utilizzando i cosiddetti marcatori di formattazione, come ad esempio %Y
per l'anno, %m per il mese e così via. Ciò consente di visualizzare le date e gli
orari in modo leggibile per gli utenti finali.
In poche parole, il modulo datetime è uno strumento essenziale per gestire il
tempo in Python, consentendo di eseguire operazioni comuni in modo efficiente e
di connettere i nostri listati allo scorrere del tempo.
Ecco un esempio esplicativo di come utilizzare il modulo datetime:

import datetime
from datetime import timedelta
import locale

# Impostiamo la lingua italiana


locale.setlocale(locale.LC_TIME, "it_IT")

# Otteniamo la data e l'ora attuali


tempo = datetime.datetime.now()
print("L'ora è:", tempo.strftime("%A %d %B %Y, %H:%M:%S"))
99
# Possiamo poi modificare la data e visualizzarla in vari modi
print("Giorno e mese:", tempo.strftime("%A %d %B"))
print("Venticinque anni fa era", tempo.year-25)

# Aggiungiamo 12 giorni alla data corrente


nelfuturo = datetime.timedelta(days=12)
nelfuturo += tempo
print("Tra dodici giorni sarà", nelfuturo.strftime("%B"))

Abbiamo utilizzato il modulo datetime per ottenere la data e l'ora attuali tramite la
funzione datetime.now(), e anche la funzione setlocale della libreria locale
impostando l’italiano per la formattazione delle date. Successivamente, abbiamo
utilizzato il metodo strftime() per visualizzare la data in formati personalizzati. In
particolare, abbiamo utilizzato i marcatori di formattazione %A per visualizzare il
giorno, %B per il mese, e %year per l'anno. Inoltre, ci siamo serviti del modulo
timedelta per aggiungere 12 giorni alla data corrente, e abbiamo visualizzato la
data risultante utilizzando nuovamente il metodo strftime().
È importante notare che i marcatori di formattazione disponibili in Python offrono
molte opzioni per la visualizzazione delle date e delle ore. Ad esempio, %H
restituisce l'ora suddivisa in 24 ore, mentre %I la mostra suddivisa in 12 ore. Di
seguito vediamo meglio gli altri marcatori:

- %A restituisce il giorno. - %a il giorno accorciato.


- %B restituisce il mese. - %b il mese accorciato.
- %H restituisce l’ora in 24h. - %I l’ora in 12h.
- %p PM oppure AM. - %Z Fuso orario.

PYTON AVANZATO - Protocolli


In questi paragrafi tratteremo due argomenti avanzati di Python che richiederanno
100
una conoscenza di base, per questo motivo i principianti potranno tornare qui in un
secondo momento.
I protocolli sono una funzionalità avanzata di Python che permette di definire
interfacce comuni tra diversi oggetti, senza bisogno di una classe base comune. In
altre parole, un protocollo definisce un insieme di metodi che un oggetto deve
implementare per poter essere utilizzato in un certo contesto, senza bisogno di
essere una sottoclasse di una classe specifica.
I protocolli sono utili in molti contesti, ad esempio quando si lavora con librerie
esterne che utilizzano interfacce comuni per interagire con diversi oggetti. In
questo modo, è possibile creare oggetti personalizzati che implementano i metodi
del protocollo e utilizzarli con la libreria esterna senza dover creare una classe
base comune. Per definire un protocollo in Python, si utilizza la sintassi del
typing.Protocol. Ad esempio, se volessimo definire un protocollo per un oggetto
che abbia un metodo area(), possiamo scrivere il seguente codice:

from typing import Protocol


class HasArea(Protocol):
def area(self) -> float:
pass

Come anticipato, abbiamo definito un protocollo chiamato HasArea, il quale


richiede che un oggetto abbia un metodo area() che restituisca un valore di tipo
float. In seguito possiamo utilizzarlo per definire funzioni o classi che richiedano un
oggetto che implementi il protocollo. Ad esempio, se vogliamo definire una
funzione che calcola l'area di un oggetto che implementa il protocollo HasArea,
possiamo scrivere il seguente codice:

def print_area(obj: HasArea) -> None:


print(obj.area())

101
In questo modo, si può utilizzare la funzione print_area() con qualsiasi oggetto che
implementa il protocollo HasArea, senza dover definire una classe base comune.
Non è ancora chiaro? Immaginiamo di avere una libreria esterna che richiede un
oggetto che abbia un metodo draw() per disegnare una figura. In questo caso,
possiamo definire un protocollo chiamato Drawable che richiede che un oggetto
abbia il metodo draw():

from typing import Protocol


class Drawable(Protocol):
def draw(self) -> None:
pass
Successivamente, possiamo creare due classi diverse, Triangolo e Rettangolo, che
implementano il metodo draw():

class Triangolo:
def draw(self):
print("Disegno un triangolo")

class Rettangolo:
def draw(self):
print("Disegno un rettangolo")

Ora, possiamo creare una funzione esterna alla classe che richiede un oggetto che
implementa il protocollo Drawable e chiamare il metodo draw() su qualsiasi
oggetto che lo implementa. Ad esempio:

def draw_shape(shape: Drawable):


shape.draw()
t = Triangolo()
r = Rettangolo()

102
draw_shape(t)
draw_shape(r)

Così facendo, grazie all'uso dei protocolli, abbiamo creato un'interfaccia comune
tra oggetti di classi diverse, la quale ci ha consentito di passare oggetti di diverse
classi alla funzione draw_shape() senza dover creare una classe base comune. In
poche parole, se un oggetto implementa tutti i metodi richiesti dal protocollo, è
possibile utilizzarlo in un contesto che richiede un oggetto di quel protocollo, senza
bisogno di definire una classe che implementi tale protocollo. Questo rende il
codice più flessibile e modulare, in quanto non è necessario creare gerarchie di
classi per ogni possibile combinazione di comportamento richiesto.

PYTHON AVANZATO - Metodi aggiuntivi sui tipi di dati


ll tipo di dati int, come già sappiamo bene, rappresenta i numeri interi e, fa parte
della classe base astratta integrale. Oltre a ciò, fornisce alcuni metodi operativi
come:

int.bit_length(): restituisce il numero di bit necessari per rappresentare un numero


intero in binario, escluso il segno e gli zeri iniziali.
int.to_bytes(lunghezza, byteorder, *, firmato=falso): restituisce una matrice di
byte che rappresenta un numero intero.
classmethod int.from_bytes(bytes, byteorder, *, signed=False): restituisce il
numero intero rappresentato dalla matrice di byte.
int.as_integer_ratio(): restituisce una coppia di numeri interi il cui rapporto è
esattamente uguale al numero intero originale con un denominatore positivo.

Il tipo float fornisce i seguenti metodi aggiuntivi:

float.as_integer_ratio(): restituisce una coppia di numeri interi il cui rapporto è

103
esattamente uguale al float originale e con un denominatore positivo.
float.is_integer(): dà True se l'istanza float è un valore intero, o altrimenti False.
float.hex(): restituisce una rappresentazione di un numero a virgola mobile come
stringa esadecimale. Per i numeri in virgola mobile finiti, questa rappresentazione
includerà sempre uno 0x iniziale e un p con esponente finale.
classmethod float.fromhex(s): restituisce il numero in virgola mobile (float)
rappresentato da una stringa esadecimale s.

Quiz & Esercizi


1) Scrivere un'espressione che calcola il valore del prodotto tra due numeri interi.
2) Scrivere un'espressione che calcola il valore dell'operazione di esponenziazione
tra due numeri interi a e b.
3) Scrivere un'espressione che concatena due stringhe s1 e s2.
4) Scrivere un'espressione che verifica se un numero intero x è pari o dispari.
5) Scrivere un'espressione che calcola il valore dell'area di un cerchio di raggio r.
6) Scrivere un'espressione che calcola la somma dei quadrati di due numeri interi
a e b.
7) Scrivere un'espressione che calcola il valore della media aritmetica di una lista
di numeri n.
8) Scrivere un'espressione che restituisce il valore massimo di una lista di numeri.
9) Scrivere un identificatore valido e uno non valido.
10) Qual è la convenzione di naming per le variabili e per le costanti in Python?
11) Qual è l'identificatore riservato in Python per indicare una funzione lambda?
12) Scrivere un programma che richiede all'utente d’inserire un numero intero e
stampa il suo quadrato. In caso d’inserimento di un valore non valido, il
programma deve gestire l'eccezione e stampare un messaggio di errore.
13) Scrivere un programma che apre un file di testo e stampa il suo contenuto riga
per riga. In caso di errore nell'apertura del file, il programma deve gestire
l'eccezione e stampare un messaggio di errore.

104
14) Scrivere una funzione che divide due numeri e gestisce l'eccezione di divisione
per zero.
15) Scrivere un programma che richiede all'utente d’inserire una lista di numeri
interi e ne stampa il valore massimo. In caso d’inserimento di un valore non
valido, il programma deve gestire l'eccezione e stampare un messaggio di errore.
16) Scrivere una funzione che calcoli il reciproco di un numero e che gestisca
l'eccezione di divisione per zero usando ZeroDivisionError.
17) Scrivere un programma che crea un nuovo file di testo e che scriva una serie
di righe in esso.
18) Scrivere un programma che apre un file di testo esistente, legga il suo
contenuto e ne stampi ogni riga.
19) Scrivere un programma che copia il contenuto di un file di testo in un altro file.
20) Scrivere un programma che chiede all'utente d’inserire la propria data di
nascita (nel formato GG-MM-ANNO) e calcoli l'età in giorni, mesi e anni. Il
programma deve anche calcolare il giorno della settimana in cui l'utente è nato.
21) Qual è la funzione utilizzata per aprire un file in Python?
22) Qual è il metodo utilizzato per creare una nuova directory in Python?
23) Come si legge un file di testo riga per riga in Python?
24) Qual è il metodo utilizzato per ottenere il percorso assoluto di un file?
25) Qual è il metodo utilizzato per scrivere su un file di testo in Python?
26) Come si calcola la differenza tra due date utilizzando il modulo datetime?
27) Qual è il metodo utilizzato per ottenere il timestamp corrente in Python?
28) Scrivere un listato che richieda all'utente d’inserire un numero intero, e di
stampare su schermo "Il numero inserito è pari" se il numero è pari, "Il numero
inserito è dispari" se il numero è dispari.
29) Scrivere un programma che chieda all'utente d’inserire il prezzo di un prodotto
e il tasso di sconto da applicare. Calcolarne poi il prezzo scontato e stamparlo su
schermo.
30) Scrivere un programma che richieda all'utente di inserire un numero intero e

105
di verificare se è pari o dispari. Stampare un messaggio appropriato, a seconda del
caso.

Riassunto
Nel capitolo, abbiamo esplorato il concetto di espressione, che è uno degli elementi
costitutivi di tutti i più importanti dei linguaggi di programmazione. Un'espressione
descrive operazioni tra oggetti. Di solito, l'obiettivo del loro utilizzo è quello di
costruire un nuovo oggetto necessario per la fase successiva dell'elaborazione (ad
esempio, il calcolo della somma del prezzo dei prodotti).
Le istruzioni descrivono una fase di un programma e apportano modifiche al
contesto di esecuzione. L'istruzione di assegnazione, che assegna un oggetto a un
nome, ne è un buon esempio. Un'espressione può fungere da affermazione, ma le
asserzioni non possono stare dove le espressioni sono previste.
Il nome della variabile è il primo strumento per organizzare il tuo programma. Ad
esempio, un'espressione complessa può essere suddivisa in diverse espressioni più
semplici i cui risultati vengono assegnati a nomi di variabili e quindi combinati in
un passaggio finale.
Abbiamo poi parlato della gestione delle eccezioni per prevenire malaugurati errori
nei nostri listati, della gestione dei file e delle directory e, infine, della gestione del
tempo con il modulo datetime.
C’è anche stata una piccola parentesi su un paio di argomenti avanzati di Python,
cioè i protocolli, utili a definire un’interfaccia comune tra oggetti differenti e alcuni
metodi aggiuntivi sui tipi di dati.

106

Potrebbero piacerti anche