Python Nuovo Bonus
Python Nuovo Bonus
Di Tony Chan
1
© Copyright 2023 - Tutti i diritti riservati.
Avviso legale:
_______________________
2
# L’autore
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
5
Ereditarietà 215
6
7
# Introduzione
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.
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.
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.
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).
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.
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.
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.
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:
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
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
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:
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:
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:
- 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
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'>
- 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:
print("Mar" + "io")
a = "Mario "
37
b = " la birra "
c = " adora "
d= " e odia "
e= " l acqua"
print(a + c + b + d + e)
print("Mario"*30)
- 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":
- 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'))
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)
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 |:
41
Metodo Utilizzo Esempio
Oltre a questi vi sono dei metodi aggiuntivi per delle operazioni molto specifiche:
uno.intersection_update(due) # {'Mario'}
- symmetric_difference() ritorna un nuovo set con tutti gli elementi degli altri
due meno quelli doppi:
42
- symmetric_difference_update() aggiorna un set con tutti gli elementi del
secondo meno quelli doppi:
- 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:
44
due = [1, 2, 3, 4, 5, 6]
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:
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:
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:
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:
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:
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:
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:
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:
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:
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:
51
min() Riporta il valore minimo nella tupla min(uno)
tuple() Converte una lista in una tupla lista=tuple(..)
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)
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
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.")
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:
def quadrato(x):
return x**2
numeri = [1, 2, 3, 4, 5]
risultati = map(quadrato, numeri)
56
def dispari(x):
return x % 2 != 0
numeri = [1, 2, 3, 4, 5]
risultati = filter(dispari, numeri)
print(list(risultati)) # [1, 3, 5]
numeri = [1, 2, 3, 4, 5]
risultato = reduce(somma, numeri)
print(risultato) # 15
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:
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:
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<<
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:
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:
62
x = 8+2-2*10/2
print(x) # risultato 0
x = (8+2-2)*10/2
print(x) # risultato 40
Elevamento a potenza:
x = 7
y = x + x * 2
print(y) # risultato 21
Oppure:
x = 7
x += 2
print(x) # risultato 9
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:
anni = "55"
n_var = int(anni)
print(n_var)
prezzo = float(55.5)
n_var = str(prezzo)
print(n_var)
prezzo ="cinquanta"
n_var = set(prezzo)
64
print(n_var)
prezzo ="cinquanta"
n_var = list(prezzo)
print(n_var)
prezzo = 55
n_var = float(prezzo)
print(n_var)
reale = 3
immaginario = 4
complesso = complex(reale, immaginario)
print(complesso)
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:
66
funzioni elencate di seguito, ricordandosi sempre che per utilizzarle bisognerà
utilizzare import random all’inizio del listato.
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
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:
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
for...in...(1,...):
print()
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
50*3 + 10
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):
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:
(0+2j)**2 == -4 # True
76
y < x and x < 5
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.
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:
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:
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:
- 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:
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")
# 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 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")
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.
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!")
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)
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.
- 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)
92
f = open("esempio.txt", "r")
open("directory/esempio.txt")
prova = open("esempio.txt")
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:
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:
Per scrivere sul file, si utilizza il metodo write() dell'oggetto file, che prende come
argomento la stringa da scrivere sul file. Ad esempio:
93
scrivi.write("Ciao da Mario!")
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
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)
Ci siamo serviti del modulo time solamente per inserire delle pause tra le varie
operazioni.
import os
95
# Creazione di una nuova directory
os.mkdir('Prova')
print("Ecco l’elenco dei file in Prova:", os.listdir('Prova'))
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
# 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")
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().
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
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:
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():
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:
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.
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.
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