Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Introduzione alla Programmazione con Python: Cicli, Funzioni e Liste, Dispense di Fondamenti di informatica

Una panoramica introduttiva alla programmazione con python, concentrandosi su concetti fondamentali come i cicli, le funzioni e le liste. L'utilizzo dei cicli for, le diverse tipologie di parametri nelle funzioni, le operazioni sulle liste e la gestione delle matrici tramite liste di liste. Inoltre, vengono presentati esempi pratici per comprendere meglio l'applicazione di questi concetti.

Tipologia: Dispense

2024/2025

In vendita dal 27/01/2025

isab.17
isab.17 🇮🇹

4

(1)

10 documenti

1 / 28

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
FONDAMENTI DI INFORMATICA – PROGRAMMAZIONE IN PYTHON
PROGRAMMAZIONE IN PYTHON – problem solving
19.09.2024
Il primo ad essersi occupato di PROBLEM SOLVING è il matematico ungherese George Polya il quale era
abituato a dimostrare teoremi e scrivere libri su quelle dimostrazioni. Il libro “How to solve it” è il primo libro
che si è occupato del problem solving ed è proprio di Polya. Il ciclo del problem solving si compone di quattro
passaggi:
- See
- Plan
- Do
- Check
Si prenda come esempio “trovare la diagonale di un parallelepipedo rettangolo di cui sono note la lunghezza,
la larghezza e l’altezza”.
Per risolvere il problema il processo non è del tutto lineare. Il primo aspetto da curare
è il SEE ovvero curare l’analisi del problema, creare un modello che possa facilitare
la risoluzione. Realizzare il modello del problema vuol dire rappresentare in maniera
astratta i dati del problema: quali sono i dati iniziali, quali sono le incognite da trovare
e le condizioni sotto le quali lavorare. Nel caso del parallelepipedo, disegnare una
buona figura può essere importante per trovare una soluzione: dal disegno si capisce
che è possibile applicare il teorema di Pitagora per trovare z e poi applicare lo stesso
teorema per ottenere d.
Dopo aver trovato un buon modello si passa alla fase PLAN ovvero si elabora un progetto, un algoritmo
risolutore che metta in relazione i dati iniziali e le incognite da trovare. Ciò che si utilizza è il pensiero
computazionale ovvero l'insieme dei processi mentali coinvolti nella formulazione di un problema e della sua
soluzione. Alcuni esempi di pensiero computazionale solo la riduzione (se non so risolvere il problema parto
da uno più semplice che so già risolvere), l’analogia (adatto il problema o la soluzione), il divide et impera (se
è il problema è complesso lo divido in problemi più semplici), la composizione (se so risolvere più problemi
semplici allora poi li unico per crearne uno più complesso), l’astrazione. Il consiglio che da Polya è quello di
cominciare da problemi più semplici per poi andarne a risolvere di più complessi.
Una volta che la soluzione è ormai nella mente si passa alla fase DO ovvero si va a realizzare questa idea sotto
forma di codice, si implementa il progetto realizzando un sistema sperimentale. Una volta scritta la soluzione
va poi controllata, CHECK, per verificare che sia corretta e magari se sia possibile scriverla in un modo
migliore (a volte una soluzione più breve e chiara si ottiene solo dopo più interazioni). La cosa più importante
in questa fase è verificare se effettivamente il problema sia stato risolto.
Nell’informatica, la soluzione ad un problema è un ALGORITMO. L’algoritmo è un procedimento che risolve
un problema attraverso un numero finito di passaggi elementari. L’algoritmo si applica su dei dati iniziali
(istanza del problema), intermedi e finali (soluzione), si realizza tramite passi elementari ovvero azioni
atomiche non scomponibili in azioni più semplici e crea un processo ovvero una sequenza ordinata di passi da
seguire. Gli algoritmi possono essere rappresentati secondo diagrammi di flusso ovvero una rappresentazione
grafica formata da nodi e archi che collegano i nodi. Ci sono tre tipologie di nodi:
- Operazioni I/O che sono azzurri
- Operazioni aritmetico-logiche che sono grigie
- Controllo del flusso di esecuzione che sono gialli
Il diagramma di flusso è molto chiaro da vedere ma non è lo strumento di progettazione consigliato perché,
data la sua estrema semplicità, non è adatto a risolvere problemi complessi, è troppo elementare.
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c

Anteprima parziale del testo

Scarica Introduzione alla Programmazione con Python: Cicli, Funzioni e Liste e più Dispense in PDF di Fondamenti di informatica solo su Docsity!

FONDAMENTI DI INFORMATICA – PROGRAMMAZIONE IN PYTHON

PROGRAMMAZIONE IN PYTHON – problem solving 19.09. Il primo ad essersi occupato di PROBLEM SOLVING è il matematico ungherese George Polya il quale era abituato a dimostrare teoremi e scrivere libri su quelle dimostrazioni. Il libro “How to solve it” è il primo libro che si è occupato del problem solving ed è proprio di Polya. Il ciclo del problem solving si compone di quattro passaggi:

  • See
  • Plan
  • Do
  • Check Si prenda come esempio “trovare la diagonale di un parallelepipedo rettangolo di cui sono note la lunghezza, la larghezza e l’altezza”. Per risolvere il problema il processo non è del tutto lineare. Il primo aspetto da curare è il SEE ovvero curare l’analisi del problema, creare un modello che possa facilitare la risoluzione. Realizzare il modello del problema vuol dire rappresentare in maniera astratta i dati del problema: quali sono i dati iniziali, quali sono le incognite da trovare e le condizioni sotto le quali lavorare. Nel caso del parallelepipedo, disegnare una buona figura può essere importante per trovare una soluzione: dal disegno si capisce che è possibile applicare il teorema di Pitagora per trovare z e poi applicare lo stesso teorema per ottenere d. Dopo aver trovato un buon modello si passa alla fase PLAN ovvero si elabora un progetto, un algoritmo risolutore che metta in relazione i dati iniziali e le incognite da trovare. Ciò che si utilizza è il pensiero computazionale ovvero l'insieme dei processi mentali coinvolti nella formulazione di un problema e della sua soluzione. Alcuni esempi di pensiero computazionale solo la riduzione (se non so risolvere il problema parto da uno più semplice che so già risolvere), l’analogia (adatto il problema o la soluzione), il divide et impera (se è il problema è complesso lo divido in problemi più semplici), la composizione (se so risolvere più problemi semplici allora poi li unico per crearne uno più complesso), l’astrazione. Il consiglio che da Polya è quello di cominciare da problemi più semplici per poi andarne a risolvere di più complessi. Una volta che la soluzione è ormai nella mente si passa alla fase DO ovvero si va a realizzare questa idea sotto forma di codice, si implementa il progetto realizzando un sistema sperimentale. Una volta scritta la soluzione va poi controllata, CHECK , per verificare che sia corretta e magari se sia possibile scriverla in un modo migliore (a volte una soluzione più breve e chiara si ottiene solo dopo più interazioni). La cosa più importante in questa fase è verificare se effettivamente il problema sia stato risolto. Nell’informatica, la soluzione ad un problema è un ALGORITMO. L’algoritmo è un procedimento che risolve un problema attraverso un numero finito di passaggi elementari. L’algoritmo si applica su dei dati iniziali (istanza del problema), intermedi e finali (soluzione), si realizza tramite passi elementari ovvero azioni atomiche non scomponibili in azioni più semplici e crea un processo ovvero una sequenza ordinata di passi da seguire. Gli algoritmi possono essere rappresentati secondo diagrammi di flusso ovvero una rappresentazione grafica formata da nodi e archi che collegano i nodi. Ci sono tre tipologie di nodi:
  • Operazioni I/O che sono azzurri
  • Operazioni aritmetico-logiche che sono grigie
  • Controllo del flusso di esecuzione che sono gialli Il diagramma di flusso è molto chiaro da vedere ma non è lo strumento di progettazione consigliato perché, data la sua estrema semplicità, non è adatto a risolvere problemi complessi, è troppo elementare.

La PROGRAMMAZIONE STRUTTURATA è rappresentata da blocchi di codice organizzati in maniera sistematica ed è formata da sole tre tipologie:

  • Sequenza: un blocco dopo l’altro
  • Selezioni ( if ): se la condizione è vera si prosegue con l’operazione 1, se la condizione è falsa si prosegue con l’operazione 2 e poi il flusso si riunisce continuando dallo stesso punto
  • Interazione ( while ): è un ciclo per cui se la condizione è vera il blocco viene eseguito e si torna indietro per poter permettere di ripetere la stessa operazione fin tanto che la condizione è vera. Si crea una condizione di permanenza del ciclo per cui fintanto che la condizione è vera si continua a ripetere il ciclo, quando diventa falsa invece si esce dal ciclo. Se la condizione è subito falsa si salta il blocco. Con queste tre tipologie di strutture si riesce a risolvere qualsiasi algoritmo. Tutte le strutture hanno in comune il fatto che c’è un unico punto di ingresso e un unico punto di uscita. ESEMPIO: nella quotidianità, l’esempio migliore di algoritmo sono le ricette. “Se non c’è il lievito usare due cucchiaini di bicarbonato” è una selezione if, “battere gli albumi finche non montano” è un ciclo while. PROGRAMMAZIONE IN PYTHON – introduzione a Python PYTHON è un linguaggio nato negli anni 90 insieme a Java anche se non è stato sostenuto sin da subito dalle grandi aziende informatiche. È un linguaggio che si è sviluppato underground, un linguaggio che si è sviluppato grazie ad internet e che in parte ha sostenuto internet stesso. In nome di questo linguaggio nasce da un gruppo comico inglese degli anni 70/80. Python è ora molto usato soprattutto negli USA ma fino a qualche anno fa non era un linguaggio così comune. Python non è solo un semplice linguaggio per imparare a programmare ma è anche lo strumento alla base di tantissimi sistemi: web, giochi, machine learning, scripting…. Python è una sorta di calcolatrice scientifica molto più potente. A prima vista presenta due aree: la prima area in alto è lo script di Python ovvero l’area dove si scrive il programma, la seconda area in basso è una shell interattiva con un’interfaccia interattiva Repl. È importante distinguere i diverti tipi di dati che si immettono in Python:
  • Dati int: numeri interi
  • Dati float: numeri con la virgola

Python risolve le operazioni base (i simboli sono gli stessi della matematica quindi +, - , *, / ) ma può anche

fare calcoli più complessi: // rappresenta la divisione intera (se scrivo 5/3 il risultato è 1.66667, se scrivo 5//

in risultato è 1), % rappresenta il resto della divisione (se scrivo 5%3 il risultato è 2 ovvero il resto), **

rappresenta la potenza (se scrivo 2**3 il risultato è 8). Tutti i calcoli di Python hanno come unico limite quello

della memoria del computer. Il simbolo # permette di iniziare un commento descrittivo.

Oltre ai dati numerici ci sono altre tipologie di dati. Per i VALORI BOOLEANI c’è il tipo bool che può assumere soltanto due valori: True o False (con l’iniziale maiuscola). A True è associato il numero 1 e a False

è associato lo 0. Si possono anche utilizzare gli operatori logici che sono and, or, not che seguono le tabelle

di verità:

  • L’operatore not: restituisce il valore inverso rispetto a quello in entrata (not True restituisce False)
  • L’operatore and: restituisce True se tutti gli elementi sono True
  • L’operatore or: restituisce True se almeno uno dei due elementi è True A B A and B A or B not A False False False False True False True False True True True False False True False True True False True False

I valori booleani si possono ottenere anche come risultati dei confronti: == controlla l’uguaglianza (1 == 1

restituisce True, 1 == 2 restituisce False), != controlla la disuguaglianza (1!=3 restituisce True, 1!=1 restituisce

vedere l’appartenenza di un elemento e len per valutare quanti elementi sono presenti nella lista. Ci sono due funzioni fondamentali in Python che permettono di chiedere e mostrare dei valori all’utente. La prima è la funzione INPUT che prende una riga di testo inserita dall’utente in una variabile, consente all’utente di immettere dati in tastiera, dati che poi verranno utilizzati dal programma. Quando si inserisce la funzione input essa blocca il programma finchè non è l’utente a scrivere qualcosa (ovviamente una stringa) e poi a premere invio. Se si desidera immettere valori numerici o di altri tipi, è possibile convertire la stringa restituita da input usando funzioni come int o float. La funzione PRINT serve per mostrare l’output all’utente, scrive una serie di valori su una riga inserendo lo spazio tra i parametri, i valori. ESEMPIO: si chieda all’utente come si chiama e quanti anni ha. PROGRAMMAZIONE IN PYTHON – moduli di Python (g2d) Il modulo permette anche di fare dei DISEGNI sullo schermo che non è altro che una griglia di pixel che possono essere colorati. Per capire a quale pixel fare riferimento si può usare una coppia di coordinate ma, a differenza del piano cartesiano ordinario, l’origine degli assi è in alto a sinistra quindi le x crescono verso destra ma le y crescono verso il basso ed inoltre gli indici di riga e colonna partono da 0 (se le colonne o le righe sono 50 vuol dire che l’ultima colonna o riga è la 49 perché è conteggiata anche la prima che è la zero). Per colorare un punto bisogna specificare il punto secondo il modello dei colori: i tre colori primari sono rosso, verde e blu e dalla combinazione di questi si ottengono quelli secondari (rosso + verde dà il giallo, rosso + blu dà il magenta, blu + verde dà il ciano, rosso + verde + blu dà il bianco). I colori hanno un range che va da 0 a

  1. Per fare i disegni utilizziamo il modello g2d. Il tipo tuple di Python rappresenta un insieme di elementi definiti utilizzando una coppia di parentesi tonde dove gli elementi all’interno sono separati da delle virgole. Le tuple sono sequente immutabili di valori anche di tipo diverso. Le tuple sono utili anche per la grafica perché permettono di individuare diverse cose:
- Posizione: le coordinate di un punto di scrivono (x, y) - Dimensione: la dimensione di un oggetto è (larghezza, altezza) - Colore: per specificare il colore di un determinato oggetto si crea una tupla di tre valori 

Per quanto riguarda i colori la tupla è tripla ed è così formata: (rosso, verde, blu). Alcuni esempi standard: - (255, 0, 0) → colore rosso - (0, 255, 0) → colore verde - (0, 0, 255) → colore blu - (255, 255, 0) → colore giallo - (255, 0, 255) → colore magenta - (0, 255, 255) → colore ciano - Combinando poi i diversi colori se ne possono ottenere tanti altri. Prima di scrivere qualsiasi cosa bisogna importare g2d dunque la prima linea deve riportare la dicitura “import g2d”. successivamente bisogna inserire le dimensioni dello schermo sul quale si vuole ottenere qualcosa quindi inserire “g2d.init_canvas((larghezza, altezza))”. Pr i colori si digita “g2d.set_color((red, gree, blu))” con le varie componenti di colore. Poi è possibile disegnare e scrivere: - g2d.draw_rect((punto di inizio), (misura))

  • g2d.draw_circle((centro), raggio)
  • g2d. draw_line((punto di inizio), (punto di fine))
  • g2d.draw_line(‘testo’, (centro), grandezza del font) Dopo aver scritto ciò che si vuole disegnare bisogna terminare la scrittura con “g2d.main_loop()” che permette di avviare il ciclo di eventi. g2d.prompt è un’alternativa a input mentre g2d.alert è un’alternativa a print. PROGRAMMAZIONE IN PYTHON – altri moduli di Python Phyton ha in sé molti moduli già predefiniti. Il modulo MATH è uno di questi e, per poterlo usare, è importante scrivere “import math”. All’interno di math ci sono tutte le scritte tipiche della matematica e per poterle usare è bisogna scrivere math.funzione da utilizzare (math.sqrt(), math.log()…). È possibile non scrivere ogni volta math. funzione scrivendo prima di ogni cosa from math import sqrt (o qualsiasi altra funzione). In math è importante anche la dicitura inf (oppure - inf) che rappresenta l’infinito. È possibile quindi fare dei confronti con l’infinito. Un altro modulo di Python è RANDOM che serve a generare dei numeri casuali. Valgono le stesse regole di math. Ci sono alcuni operatori che possono essere importanti:
  • randint(…,…): estrae un parametro a caso tra i due inseriti che vengono considerati il minimo e il massimo (sono compresi anche min e max tra quelli che possono uscire)
  • randrange(…): estrae un parametro a caso dallo 0 (incluso) al parametro indicato (escluso)
  • choice([…,…,…]): estrae un elemento da una sequenza qualsiasi 26.09. PROGRAMMAZIONE IN PYTHON – strutture di controllo Python In Python ci sono dei blocchi per fare la programmazione strutturata. Per fare una selezione c’è l’istruzione IF che richiede una condizione booleana (o vera o falsa). Il corpo dell’if si scrive nel seguente modo: condizione booleana, due punti, a capo si inserisce il corpo dell’if spostato a destra di 4 spazi. Il corpo dell’if rappresenta l’istruzione da seguire solo nel caso in cui la condizione sia vera ed è una condizione da inventare. Se istruzioni che non fanno parte del corpo dell’if vengono scritte senza la spaziatura, segno che non fanno parte dell’if. Se la condizione è vera si esegue il corpo dell’if, se la condizione è falsa non lo si esegue, ciò che viene dopo si esegue in entrambi i casi. ESEMPIO: si chieda all’utente un raggio. Se r è maggiore o uguale a 50 e minore o uguale a 90 allora si disegna un cerchio blu. Se r non è parte del range indicato si disegna un cerchio giallo. L’utente decide un raggio: se r <= 90 e r => 50 allora si disegna un cerchio blu (si esegue il corpo dell’if) e successivamente un cerchio giallo. Se il raggio non appartiene al range indicato si salta il corpo dell’if e si disegna soltanto il cerchio giallo. All’if si può aggiungere la clausola ELSE. Se l’informazione dell’if risulta falsa allora si salta completamente il corpo dell’if e si passa all’else. L’else viene eseguito se e solo se la condizione dell’if risulta falsa e successivamente si prosegue dallo stesso punto. Un errore comune è inserire una condizione anche per l’else ma non è corretto: l’unica condizione è quella dell’if, se è versa si esegue il corpo dell’if altrimenti si esegue l’else. Esiste anche la contrazione di else e if ovvero ELIF (“altrimenti se”). L’elif è utile quando il programma si trova a fronteggiare più di due strade possibili. L’istruzione elif viene eseguita solo se la sua espressione di controllo restituisce True e se gli if o elif precedenti hanno restituito False. Di clausole elif ce ne possono essere tante quante ne servono per la programmazione. Gli elif sono importanti anche per una questione di leggibilità: continuando a scrivere else e poi if si crea un annidamento crescente a destra e la leggibilità diventa minore.

ESEMPIO: l’obiettivo è quello di disegnare una sequenza di n quadrati dove L è il lato del canvas noto mentre l è il lato dei quadrati noto. La posizione del primo quadrato è 0 (in alto a sinistra) e quella dell’ultimo quadrato è L – l. Come prima cosa si deve chiedere all’utente in numero n di quadrati da generare. Questi quadrati, a prescindere da quanti siano, devono occupare la diagonale e il loro colore deve essere sui toni del rosso partendo dal nero (saturo) fino al rosso acceso (255). In questo caso è utile il ciclo for per la sua linearità. Note la posizione del primo e dell’ultimo quadrato, gli altri vengono messi in una posizione intermedia che dipende dai passi del ciclo infatti è possibile calcolare pos_m = (pos_lst – post_fst)/max(n-1,1): l’ultimo quadrato meno il primo diviso quanti quadrati sono, il denominatore è scritto così per evitare una divisione per zero se n fosse uguale a 1. Poi si costruisce il ciclo for variabile rispetto al numero dei triangoli come in esempio. Lo stesso ragionamento fatto per la posizione lo si fa anche per il colore rosso: red_fst, red_lst = 0, 255. L’uso più comune del ciclo for utilizzato in questo modo è per gestire matrici oppure elementi grafici dove i passaggi sono ripetitivi. ESEMPIO: si vuole mostrare una griglia di rows x cols (righe per colonne) di rettangoli dove orizzontalmente il colore blu cresce da 0 a 255 e verticalmente il colore verde cresce da 0 a 255. In questo caso si devono creare due cicli annidati di valori lineari rispetto a x e y. Dopo aver definito la misura del canvas, dei rettangoli e la gradazione del colori si crea un primo ciclo for al quale si aggiunge un secondo ciclo for che chiede di disegnare i rettangoli con colori che si mischiano. PROGRAMMAZIONE IN PYTHON – cicli con sentinella Il valore SENTINELLA (o flag) è uno o più valori di controllo che identificano un caso eccezionale, è un indicatore che fa capire quando bisogna smettere di fare una determinata cosa oppure quando modificare qualcosa nel ciclo while. La sentinella è il valore scelto tendenzialmente quando si devono acquisire dei valori input in un ciclo. Il valore sentinella deve essere diverso dai dati e scelto in modo appropriato. ESEMPIO: il valore sentinella è - 1. Inizialmente si chiede all’utente un numero, se questo è diverso da - 1 allora lo si “stampa” elevato alla terza e si chiede nuovamente all’utente un numero. Questo ciclo può continuare ininterrottamente finchè non è immesso quel valore e quindi il ciclo finisce. Il codice input è presente due volte nel: prima di cominciare il ciclo e durante il ciclo dopo aver elaborato il valore corrente. Il ciclo for e il ciclo while sono due strutture intercambiabili ma, in determinati contesti, è preferibile usarne uno piuttosto dell’altro per una questione di leggibilità e codifica: il ciclo for viene usato quando si conosce il punto di inizio e di fine del ciclo, quando si deve “ciclare” su una stringa con dimensioni finita e quando si ha a che fare con range, tuple, liste; il ciclo while si usa quando non si conoscono i dati di inizio e fine del ciclo e quando c’è un input continuo dove non si sa quante volte si deve ripetere il ciclo oppure quando ci possono essere delle condizioni che implicano l’interruzione del ciclo. ESEMPIO: l’obiettivo è calcolare il massimo tra una sequenza di valori. Si assume come valore iniziale meno infinito quindi questo valore va importato dalla libreria math. Poi si costruisce un ciclo for dove se il valore è più grande di meno infinito allora quello prende il valore più grande e viene stampato.

22.10. PROGRAMMAZIONE IN PYTHON – le funzioni Una FUNZIONE è un operatore che viene applicato a degli operandi per ottenere un certo risultato. L’utilità delle funzioni è quella di rendere l’organizzazione di un programma più modulare cercando di astrarre il problema per trovare delle soluzioni il più generali possibili. La funzione deve essere definita ed implementata: bisogna definire il nome, gli operandi su cui operare e il risultato da restituire. Dopo aver creato la funzione bisogna poi utilizzarla. La sintassi di una funzione in Python è data da: def nome della funzione (inventata, per convenzione non deve iniziare con la lettera maiuscola) e tra parentesi tonde gli operandi su cui la funzione opererà, tutto questo è seguito dai due punti e a capo, dopo il tab, ci sono le istruzioni relative alla funzione. Dopo le istruzioni, a capo, c’è return per terminare l’esecuzione e restituire il risultato. Scrivere soltanto def permette di definire la funzione ma non di eseguirlo, bisogna quindi chiamarla. La funzione, quando viene eseguita crea nuovi spazi di nomi e i parametri e le variabili hanno ambito locale e non sono visibili nel resto del programma: la funzione randint restituisce un numero tra un minimo ed un massimo inseriti senza fare sapere o vedere quali variabili ausiliarie vengono create per la sua riuscita. È importate ricordarsi di assegnare il risultato di una funzione ad una variabile. Nelle funzioni ci sono due tipologie di parametri:

  • Parametri formali: sono i nomi che vengono assegnati ai parametri della funzione in fase di definizione ed implementazione ma non necessariamente corrispondono a quelli utilizzati effettivamente durante la chiamata della funzione, questi parametri sono le uniche e sole variabili di cui si ha disponibilità all’interno della funzione (a meno di variabili globali), sono i parametri su cui la funzione lavora. I nomi utilizzati in questi parametri devono essere il più generali possibili perché sono quelli utilizzati quando la funzione viene definita
  • Parametri effettivi: sono i parametri utilizzati effettivamente durante la chiamata della funzione, durante il suo vero utilizzo. I parametri hanno dei veri e propri valori Tra tutte le funzioni, esiste la funzione main ovvero la funzione principale che serve per identificare che all’interno di quella funzione c’è il programma principale. La particolarità della funzione main è che è priva di return. La funzione main ha l’utilità di modularizzare il codice per renderlo più leggibile. Quando si usa la funzione main è importante ricordarsi di richiamarla tramite main(), se questo non è fatto allora il programma non fa niente. 24.10. Se si vuole realizzare un’ ANIMAZIONE sul canvas si deve sfruttare la libreria grafica g2d. All’inizio di questo programma infatti ci sono delle variabili che vengono inizializzate a dei valori, la definizione di una funzione ed il suo richiamo. La funzione viene definita tramite la sua sintassi normale e viene chiamata tick() quindi è una funzione che non prende in ingresso nessun parametro. Le ultime due istruzioni di questo programma sono tipiche della libreria g2d: la prima inizializza il canvas e fornisce quindi le misure, la seconda è il main_loop. A differenza di tutti i main_loop utilizzati fino a questo punto, questo ha un parametro tra le parentesi ed il parametro utilizzato è proprio tick, il nome della funzione definita. Quando si definisce una funzione può essere definita con:
  • Assenza di parametri (come tick)
  • Presenza di parametri in ingresso (come la funzione ipotenusa)

le definizioni delle funzioni della libreria g2d (quando g2d non era nella stessa cartella di Python infatti il programma dava errore). Se viene creata una funzione in un file e salvo il file con il nome mymath.py, è possibile poi aprire un nuovo file, scrivere import mymath e richiamare la funzione definita al suo interno. La sintassi è nome del modulo.nome della funzione: g2d.set_color, g2d.main_loop, mymath.hypotenuse. Il file g2d quando viene importato prevede che tutto ciò che è scritto al suo interno venga copiato, incollato ed eseguito. Siccome un file .py può essere sia una libreria sia un programma, per evitare comportamenti anomali Python prevede l’istruzione if _ _ name_ _ == “_ _ main_ _” che è una clausola di controllo che permette al codice di capire se è eseguito come modulo oppure come script: se c’è questo comando vuol dire che il modulo viene importato senza eseguire il main, altrimenti si esegue tutto il codice. Quando si scrive il programma in Python, l’interprete analizza il file per trovare eventuali errori ma gli unici che può trovare sono quelli strettamente di sintassi ma non è in grado di stabilire a priori se una funzione darà un errore o un risultato sbagliato. Siccome non esiste un modo per sapere a priori se ci sarà l’errore e siccome non si vuole che il programma si interrompa, può essere fatta una gestione degli errori manuale tramite l’istruzione raise : se si verifica una condizione sbagliata, invece che lasciare all’interprete di trovare il problema bloccando l’esecuzione, si gestisce l’errore manualmente stampando il messaggio di errore. Il vincolo dell’istruzione di errore raise è che l’utente che scrive il programma deve sapere quando si potrà verificare l’errore. Nel caso dell’esempio: poiché un triangolo è tale se un lato è minore della somma degli altri due, se si verifica questa condizione allora viene restituito il perimetro altrimenti viene mostrato il messaggio di errore. Le funzioni possono prendere in ingresso qualunque tipo di dato, qualunque numero e la stessa cosa vale per i valori di ritorno: possono essere uno, più di uno separati da virgole oppure tuple. Una funzione può anche presentare più istruzioni di return, non è però una buona prassi. L’utilità delle funzioni è quella di rendere tutto più modulare ma un suo limite è quella di fornire limitata astrazione, incapsulare un comportamento. È importante mettere in chiaro i parametri e i dati che servono: quando si scrive una funzione, quest’ultima vede i dati che sono riportati al suo esterno e li può modificare con le variabili globali ma questo può portare a delle complicazioni. ESEMPIO: si vuole definire una funzione per rimbalzi ovvero, quando la pallina animata tocca il bordo del canvas, rimbalza cambiando direzione. Dopo aver importato g2d e aver definito una serie di variabili, si definisce la funzione move_ball che permette di muovere la pallina all’interno del canvas: se la x non è compresa nel range dato allora cambia direzione e lo stesso vale per la y, ciò che viene restituito è una tupla di 4 valori. Si definisce poi la funzione tick che non prende in ingresso nessun valore ma che al suo interno prevede di avere delle variabili globali. Come ultimo step viene definita la funzione main che prevede di disegnare il canvas, e il main_loop(tick), chiamando main() la pallina rimbalza quando tocca i lati del canvas. La funzione deve essere sempre definita nel modo più lineare possibile facendo in modo che non siano presenti istruzioni che possano determinare comportamenti imprevedibili quando vengono eseguiti. Ci sono però due casi in cui questo può accadere:

  • Nella funzione sono previste operazioni di input/output
  • Si lavora con variabili globali (imprevedibili) In uno dei due casi precedenti è possibile avere degli EFFETTI COLLATERALI ovvero avere dei comportamenti diversi della funzione: chiamata più volte con gli stessi parametri, la funzione può restituire

valori diversi. Quando sono presenti più funzioni una dentro l’altra, quella che viene eseguita per prima è quella più interna esattamente come nelle espressioni matematiche. Gli effetti collaterali però possono trasformarsi in problemi non restituendo il valore corretto della funzione. 29.10. Esistono anche delle FUNZIONI TRIGONOMERICHE. Un punto può essere definito da coordinate polari o da coordinate cartesiane: le coordinate polari sono definite come la lunghezza del segmento e di un angolo rispetto al riferimento mentre le coordinate cartesiane sono le coordinate x e y nel piano. Per fare la conversione tra queste due tipologie si usa il seno e il coseno dell’angolo compreso tra un asse di riferimento. Da polari a cartesiane si usa il seguente sistema: {

e da cartesiane a polari: {

𝑟 = √𝑥^2 + 𝑦^2

Questo concetto può essere sfruttato sul canvas per tracciare segmenti in forme complesse oppure per definire il movimento di un oggetto. ESEMPIO: l’obiettivo è tracciare una serie di segmenti che hanno in comune l’origine e che differiscono tra loro di un certo angolo. Dopo aver importato la libreria grafica g2d e i moduli che servono dalla libreria math viene definita una funzione che prende in ingresso tre valori interi: il punto di partenza, il punto di arrivo e la lunghezza della linea. Nella definizione della funzione è anche presente un ciclo for che permette di disegnare una linea per ognuno degli angoli digitati: in questo caso disegnerà 4 raggi dove il primo sarà orizzontale (angolo 0°), il secondo di 15°, il terzo di 30° e il quarto di 45°. Le ultime istruzioni date permettono di definire le dimensioni del canvas e disegnare i raggi. La funzione definita è generale ma risponde ad un problema specifico. L’obiettivo delle funzioni sarebbe quello di essere il più generali possibili quindi si vorrebbe che questa funzione funzionasse anche nel caso di coordinate polari e non sono nel caso di coordinate cartesiane. Tramite il simbolo = avviene l’assegnazione di un nome di una variabile ad un valore. Tramite questo operatore si possono anche definire nuove tipologie di dato che sono combinazioni dei tipi di dato base. Point e Polar sono due nuovi punti costituiti entrambi da una tupla formata da due float. Definire queste due tipologie di dato permette una maggiore leggibilità ed una maggior possibilità di generalizzare: nel caso di Point i due float rappresentano le coordinate cartesiane x e y, nel caso di Polar i due float rappresentano il raggio e l’angolo delle coordinate polari. ESEMPIO: l’obiettivo è scrivere la funzione precedente utilizzando altri moduli che generalizzino ulteriormente la funzione: vengono convertite le coordinate da polari a cartesiane e viene calcolata la posizione del finale dato il punto di partenza, la lunghezza e l’angolo. La prima funzione che viene definita restituisce le coordinate cartesiane partendo da quelle polari e le restituisce sotto forma di Point ovvero una tupla di due float. La funzione move_arount prende in ingresso il punto di inizio (dato di tipo Point), la lunghezza e l’angolo (dati di tipo float) e restituisce un punto ovvero il punto di partenza sull’asse x più il suo spostamento sull’asse x e il punto di partenza sull’asse y più il suo spostamento sull’asse y. Volendo definire draw_rays (la funzione dell’esempio precedente) tramite move_around si deve chiamare questa funzione dando in ingresso le coordinate iniziali sotto forma di tupla, il raggio e l’angolo (gli angoli sono quelli citati nel ciclo for) e ciò che viene disegnato è una linea. In definitiva si ottengono una serie di raggi con vertice in comune e ampiezza che varia a seconda degli angoli inseriti. L’ultima istruzione è la definizione della funzione main che prevede di disegnare il canvas, i raggi e il main_loop.

ESEMPIO: prendendo in considerazione l’esempio precedente delle palline che rimbalzano, l’obiettivo è quello di eliminare le variabili globali e avere meno dati in ingresso e in uscita. Dopo aver importato la libreria grafica g2d (unica cosa uguale) viene definita la classe Ball secondo la sua sintassi corretta. Il primo metodo della classe è il metodo di inizializzazione dove come parametri prende self (ordinario) e x0, y0. Self, in questo caso, si riferisce alla pallina. Per ogni pallina è bene definire la posizione iniziale, finale e la velocità sugli assi x,y (esattamente le variabili dell’esempio precedente). Poiché non avrebbe senso che tutte le palline partissero dallo stesso punto, le posizioni non sono fisse e quindi occorre definire i parametri x0 e y0 che vengono poi riportati anche nella parentesi della definizione. In questo caso si è deciso che le velocità sono invece le medesime per tutte le palline quindi, durante la definizione dei campi si associa direttamente un numero e non un parametro (se si fossero scelte velocità diverse per le palline allora bisognava creare altri parametri e aggiungerli alla parentesi). Dopo aver definito questa classe è possibile definire b1 e b2 che sono le palline (potrebbero essere n, un numero a piacere) con una nuova sintassi: b1 = Ball(n,n) dove Ball è il nome della classe e i valori sono x0 e y0 ovvero i parametri che sono stati aggiunti da me, se non avessi aggiunto parametri dopo il self allora le parentesi sarebbero rimaste vuote. Viene definita un’ulteriore funzione all’interno della classe Ball che deve permettere il movimento delle palline. Inizialmente c’erano una serie di if: uno per le x della pallina 1, uno per le x della pallina 2, uno per le y della pallina 1 e uno per le y della pallina 2. Nel nuovo codice definisco la funzione move che prende in ingresso il parametro self (obbligatorio) e deve restituire una tupla di quattro valori. I cicli if si riducono a due perché non c’è più la distinzione tra prima e seconda pallina, il ciclo serve per le x e poi per le y di tutte le palline che verranno secondariamente generate. Tutte le diciture x del primo codice vengono sostituite da self._x e lo stesso vale anche per y, dx e dy. Dopo aver definito i due cicli if che permettono i rimbalzi (quando la pallina tocca i bordi del canvas cambia direzione), devo aggiungere l’indicazione che le componenti x e y si devono aggiornare. Per poter fare questa cosa nel codice precedente avevo necessità di utilizzare le variabili globali, ora non più. Si può quindi poi definire b1.move e b2.move che permetteranno il movimento della pallina, all’interno delle parentesi non viene espresso nessun parametro perché, nella definizione di move all’interno della classe, l’unico parametro utilizzato è self. Viene infine definito il metodo pos all’interno della classe Ball, metodo che prende in ingresso solo il parametro self e restituisce una tupla di due valori int ovvero la posizione della pallina. Questo metodo deve produrre un risultato quindi necessità del return. Non è possibile scrivere soltanto b1.pos() ma a questo valore deve essere assegnata una variabile che permette poi di utilizzare il risultato. La variabile può essere chiamata pt1 (come in questo caso) e corrisponde ad una tupla oppure si può spacchettare la variabile nei due valori della tupla. La funzione tick è molto più chiara e leggibile perché i valori rimangono incapsulati all’interno della classe. Nella funzione tick viene pulito il canvas, definita la posizione delle palline e, tramite questa posizione, disegnate le stesse. Infine, si fanno muovere le palline come riportato dal metodo move. Le ultime due istruzioni sono tipiche di g2d e permettono di inizializzare il canvas con le sue dimensioni e il main_loop che prende in ingresso la funzione tick. Inizialmente il programma era dotato di una serie di variabili globali che, con la programmazione per oggetti, non servono più. Il codice risulta quindi più pulito e leggibile, non ci sono tanti dati in giro da gestire.

Il parametro SELF che viene inserito come primo parametro in ogni metodo è un valore che viene assegnato automaticamente da Python, self è anche l’oggetto su cui si va a fare una determinata operazione. Il sistema si crea un oggetto in memoria per la classe che è stata definita e crea anche una raccolta generica di informazioni sull’oggetto. Quando si scrive un programma, si scrive il metodo init ma non viene chiamato, è Python a fare questa cosa. Quindi:

  • Campi: memorizzano i dati caratteristici di una istanza, nell’esempio fatto ogni pallina aveva una sua posizione e velocità (self._x, self._y, self._dx, self._dy)
  • init: metodo costruttore, è sempre il primo metodo
  • self: è sempre il primo parametro di ogni metodo per convenzione, il suo valore è assegnato in maniera automatica e trasparente da Python ed è un parametro che rappresenta l’oggetto su cui opera il metodo
  • Parametri: passano altri valori ad un metodo se nei campi non sono presenti alcuni dati necessari
  • Variabili locali: memorizzano i risultati parziali generati durante l’elaborazione del metodo e vengono cancellati dopo l’uscita dal metodo
  • Variabili globali: variabili definite fuori da tutte le funzioni e sono variabili da utilizzare il meno possibile, è meglio avere dei parametri in più nelle funzioni piuttosto che usare le variabili globali Quando si deve lavorare con la programmazione per oggetti, si deve definire bene quali siano effettivamente i dati essenziali dell’oggetto. Se si vuole definire una retta, i coefficienti che determinano questa retta sono m e q ovvero la pendenza e l’intercetta sull’asse delle y. La x che definisce l’equazione y = mx + q non è un parametro proprio della retta, è uno degli infiniti punti della retta e permette di determinare l’ordinata corrispondente. Se si volesse rappresentare questa retta tramite la programmazione ad oggetti si potrebbe definire la classe Rect, e nel metodo init inserire self, m e q, si potrebbe poi definire un altro modello che, data una x, restituisca l’equazione della retta ovvero restituisca la y. Se si vuole rappresentare una circonferenza, i coefficienti specifici che determinano tale oggetto sono il centro ed il raggio. 07.11. ESEMPIO: il personaggio di D&D è definito dal suo nome e da dei punti ferita hp. La classe viene denominata Fighter e il personaggio è inizializzato nel metodo init tramite un nome che viene associato al personaggio stesso (self._name). I punti vita vengono generati casualmente con valori compresi tra 15 e 30 e quindi non rappresentano un parametro del metodo costruttore init. Il personaggio compie poi un percorso nel quale può essere ferito ed eventualmente curato. Se viene colpito deve subire una ferita che gli fa perdere punti in base a come viene colpito. Il metodo hit prende come parametro (oltre a self) damage ovvero il danno: nel metodo hit i punti ferita sono il massimo tra i punti ferita attuali meno il danno e il valore 0. Si deve mettere la funzione max perché in questo gioco non ha senso avere punti minori di zero. Se il personaggio viene colpito può essere curato e il metodo heal prende come parametro cure: se il personaggio è vivo self._alive() allora i punti ferita sono il minimo tra i punti attuali più la cura e il valore 30 che rappresenta il massimo. Self._alive è la chiamata del metodo alive che permette di capire se il personaggio è vivo (true) o morto (false). Per descrivere questo metodo si potrebbe usare un ciclo if: se i punti hp sono maggiori di 0 allora True, se i punti hp sono minori di 0 allora False ma, per semplicità e leggibilità, si scrive soltanto “return self._hp > 0” che è la condizione vera. L’ultimo metodo è descrive dove viene restituita una stringa che specifica il nome del personaggio e quanti ferita ha. Dopo aver definito i vari metodi della classe si può scrivere il programma effettivo e usare il personaggio: c = Figher(‘Hero’) definisce il nome del personaggio e, a seguito si stampa il metodo describe. Successivamente c’è un ciclo for dove la variabile è _ perché è una variabile che non viene effettivamente utilizzata all’interno del ciclo (è una convenzione): per tre volte il personaggio c viene colpito (metodo hit) casualmente con un

dall’icona del fantasma invisibile. Dopo aver definito i metodi della classe si può passare a scrivere il programma: la funzione tick cancella in canvas e per ogni elemento in ghosts disegna un fantasma nella posizione, con la visibilità e con la grandezza definita dai metodi e poi il fantasma viene fatto muovere. Il ciclo for significa che, per ogni elemento della lista ghosts, si fanno determinate azioni e la lista ghost è definita all’interno della funzione main. La lista creata è vuota e poi, tramite un ciclo ripetuto 5 volte, si aggiungono alla lista tramite la funzione .append() i fantasmi creati dalla classe Ghost. Nella funzione main g2d e la lista ghosts vengono definiti come funzioni globali perché vengono modificati. Le ultime istruzioni della funzione main prevedono di disegnare il canvas con le misure definite all’inizio del programma e poi c’è il main_loop che prende la funzione tick. Viene infine chiamato il main è la funzione parte. 12.11. PROGRAMMAZIONE IN PYTHON – relazioni Gli oggetti diventano molto importanti quando si riescono a comporre insieme. Due o più oggetti possono essere messi insieme se hanno un’ INTERFACCIA COMPATIBILE. Tutto ciò che è stato visto finora era utile quando si aveva un solo oggetto (per una sola pallina si usavano variabili globali) oppure tanti oggetti tutti uguali (per n palline si è usata la programmazione oggetti con l’ausilio delle liste). Quando si inizia ad avere una serie di oggetti con una base comune ma delle specializzazioni diverse si deve ricorrere ad un’astrazione superiore. In biologia si usa la classificazione e la gerarchia ed è un buon metodo: ogni sottoclasse ha le caratteristiche della classe base ma in più ha delle specializzazioni proprie. Con questo metodo è possibile lavorare a diversi livelli di astrazione. La classe base della classificazione è definita come INTERFACCIA ASTRATTA. L’interfaccia è il fatto che tutti gli oggetti facciano una determinata cosa (ad esempio tutti gli animali fanno un verso) mentre il polimorfismo è il fatto che ogni oggetto fa quella determinata cosa in modo diverso (ad esempio ogni animale fa un verso diverso). In Python, data una classe, è possibile creare una serie di sottoclassi tramite la sintassi: class Nome(nome della classe base): e definirne i metodi. Quando si creano delle sottoclassi è implicito il fatto che le nuove classi create abbiano le stesse caratteristiche della classe base. ESEMPIO: si consideri la classe Animal all’interno della quale si definite il metodo speak. Se si considera solo questa classe c’è un’astrazione troppo generale: non è possibile definire un unico verso per la categoria “animali”. Viene infatti inserito l’elemento di controllo dell’errore raise. Dopo aver definito la classe è possibile definire una serie di sottoclassi corrispondenti agli animali: ogni sottoclasse ha la sintassi propria delle sottoclassi di Python e contiene il metodo init che prende in ingresso il parametro self e name. Il campo name prevede di specificare il nome dell’animale. La sottoclasse contiene anche il metodo speak che stampa una stringa contenente il nome dell’animale e il suo verso. Dopo aver definito le classi e le sottoclassi si possono “creare degli animali” tramite l’assegnazione di una variabile ad ogni oggetto: un cane, un gatto e due maiali. È poi possibile creare una lista di animali (i parametri sono le variabili) dove, tramite un ciclo for, per ogni elemento della lista si esegue speak ovvero si stampa il nome e il corrispondente verso. Gli animali sono tutti oggetti diversi che hanno in comune la caratteristica del metodo speak. È quindi possibile gestire oggetti diversi che hanno la stessa interfaccia: animali diversi con la caratteristica comune di avere un verso. ESEMPIO: si considerino una serie di personaggi che hanno tutti la peculiarità di muoversi ma ognuno si muove in un modo diverso rispetto agli altri. La classe base ovvero l’interfaccia astratta è la classe Actor che al suo interno ha il metodo move, pos che restituisce la posizione dell’attore, size che restituisce la dimensione dell’attore e sprite. I valori “attori” sono delle sottoclassi dell’interfaccia astratta Actor e realizzano i metodi

di Actor definendo i comportamenti specifici e poi, se necessario, definiscono ulteriori metodi. Si può quindi creare una sottoclasse Ball che faccia muovere la pallina (o eventualmente più palline) e anche altre sottoclassi: Ghost, Turtle. Tutti questi personaggi che vengono creati hanno la capacità di muoversi, ognuno a modo suo. La cosa importante è avere il metodo move. 14.11. PROGRAMMAZIONE IN PYTHON – sequenze Le liste sono sequenze mutabili di elementi omogenei. A volte è necessario avere una lista con una dimensione nota e i cui valori vengano calcolati durante l’esecuzione. Per fare questo procedimento si usa la LIST REPETITION ovvero si prende una lista di elementi e la si moltiplica per il numero n: quello che si ottiene è un’altra lista che contiene gli stessi oggetti della prima ma in numero pari ad n. Questo metodo delle list repetition è utile quando n è molto grande per evitare di dover scrivere n volte lo stesso oggetto all’interno di una lista. Nell’esempio viene creata una lista di dodici zero, una lista dove i valori 1, 2, 3 sono presenti quattro volte e una lista in cui i due nomi riportati sono moltiplicati per tre volte. Sulle liste ci sono diverse funzioni: len permette di scoprire quanti elementi sono nella lista, append aggiunge un elemento alla fine, remove toglie un elemento (tutte queste funzioni sono già state trattate). È però possibile accedere ai vari valori della lista tramite il loro INDICE , passandolo tra parentesi. Gli elementi n di una lista sono numerati a partire da 0 fino a n-1: scrivendo il nome della lista e l’indice tra parentesi quadre si può ottenere l’elemento desiderato. Gli indici sono utili anche per fare delle sostituzioni: il metodo append permette di aggiungere un elemento alla fine della lista, se invece si vuole mettere un elemento al posto di un altro mantenendo invariata la dimensione della lista si usano gli indici. Gli indici prendono valori da 0 a n-1 e possono essere scritti in ordine crescente oppure in ordine decrescente: se si scrive n-3 o semplicemente - 3 si considera il terzultimo elemento della lista. Non è però possibile andare indietro a piacere, il limite è n. ESEMPIO: si consideri una lista composta dai dodici mesi dell’anno allora n = 12. Se chiedo al programma months[3] ciò che si ottiene non è il mese numero tre ma il terzo elemento della lista, considerando che il primo indice è 0: si ottiene aprile. Se si scrive months[12-3] o semplicemente months[-3] si ottiene ottobre: si deve fare 12 - 3 = 9 e il nono elemento della lista è appunto ottobre (non è il nono mese dell’anno). Se voglio sostituire il mese di novembre con la stringa ‘ mese del mio compleanno’ posso usare l’indice corrispondente a novembre ovvero 10. L’operazione che si svolge è una sostituzione, la dimensione della lista rimane invariata, n è sempre uguale a 12. È possibile prendere delle SLICE della lista ovvero delle porzioni. Per procedere alla creazione di slice si deve indicare l’indice di partenza e l’indice di arrivo all’interno di parentesi quadre e separarsi da :. Quando si considerano le slice, l’indice di fine indicato è escluso. Se si considera la lista dei mesi del precedente esempio, la slice months[2:5] restituisce tre valori (5-2 = 3) e sono i mesi corrispondenti agli indici 2, 3, 4: marzo, aprile, maggio. È possibile anche omettere uno degli indici o entrambi:

  • Se si omette l’indice di partenza si considera come primo elemento lo 0: la slice months[:3] considera gennaio, febbraio e marzo (0,1,2)
  • Se si omette l’indice di arrivo si considera come ultimo elemento n (in questo caso è incluso): la slice months[-3:] considera ottobre, novembre, dicembre (n-3, n-2, n-1)
  • Se si omettono entrambi gli indici si considera l’intera lista con gli elementi da 0 a n, si fa una copia della lista completa.

ESEMPIO: vengono riportati due esempi che comprendono al loro interno una lista. I due esempi sono apparentemente uguali ma, in realtà, restituiscono due risultati completamente diversi. Caso 1: la funzione reset prende data, la lista in ingresso, e ne esegue la funzione crea ovvero toglie tutti gli elementi della lista. Se nella funzione main si prende la lista nums e, su questa lista, si chiama la funzione reset, ciò che si ottiene è una lista vuota. La lista nums prende il posto della lista data e, su di essa, viene svolta la funzione clear, propria della funzione reset. Caso 2: la funzione reset prende data come lista in ingresso e determina che data sia pari ad una lista vuota. La funzione main prende la lista nums e su questa lista chiama la funzione reset. Ciò che si ottiene è una lista che contiene i valori 1,2,3. Siccome nella funzione di partenza reset non ci sono funzioni interne, viene solo definito che data sia una lista vuota, quando si chiama la funzione non viene eseguito nulla su nums che rimane infatti invariata. Le STRINGHE possono essere usate in modo analogo alle liste poiché sono sequenze immutabili di caratteri. Ogni lettera della stringa può essere indicata con un indice e quindi valgono le stesse proprietà viste per le liste: i valori partono dall’indice 0 fino a n-1. Anche sulle liste è possibile fare le slice, ciò che non si può fare è l’assegnamento perché le stringhe sono sequenze immutabili. È però possibile fare le concatenazioni e creare nuove stringe da quelle di partenza. È possibile avere una lista di stringhe. Se si vuole concatenare insieme le stringhe di una lista, si può usare il metodo join. La sintassi di join prevede di mettere una stringa di partenza che è il separatore (se non si vuole nessun separatore si mette la stringa vuota oppure la stringa vuota con uno spazio in mezzo) e poi, tra parentesi viene chiamata la lista. È anche possibile fare l’operazione inversa ovvero, date delle stringhe con un separatore, è possibile dividere questi elementi e creare una lista di stringhe tramite il metodo split. La sintassi di split prevede di mettere la lista di elementi, il punto, chiamare la funzione split e mettere tra parentesi la stringa separatrice. Le TUPLE possono essere usate in modo analogo alle liste poiché sono sequenze immutabili di valori anche di tipo diverso. Anche sulle tuple è possibile utilizzare gli indici ma è una metodologia sconsigliata poiché, essendo la tupla una sequenza di valori di tipo diverso, non è detto che questi valori siano omogenei e quindi potrebbero avere significati diversi. Sulle tuple è meglio fare l’ unpacking ovvero lo spacchettamento tramite l’assegnazione di variabili spieganti il significato dei vari elementi della tupla. L’unpacking si può fare su qualunque sequenza: tuple, liste, stringhe. L’unico limite dell’unpacking è che la lunghezza dello spacchettamento e quindi delle variabili scelte coincida con gli oggetti che effettivamente si stanno trattando. L’operazione inversa dell’unpacking è il packing ovvero il “pacchettamento”: date delle variabili che si riferiscono a dei valori, le si possono mettere insieme tramite tupla, lista o stringa a seconda di ciò che si vuole ottenere. Il limite è sempre che le lunghezze coincidano. Le liste sono utili come collezioni di oggetti ognuno dei quali ha una determinata posizione e un elemento della lista può comparire al suo interno anche più volte. L’ INSIEME è invece una collezione non ordinata di oggetti senza ripetizioni. Per creare un insieme si utilizzano le parentesi graffe al cui interno sono posti i valori. Si può controllare la presenza di un elemento nell’insieme tramite la funzione in ma non è possibile sapere la posizione di quel dato elemento, non ci sono gli indici negli insiemi. In controlla quindi l’appartenenza, si può aggiungere qualcosa all’insieme tramite l’operazione add e si può togliere qualche valore dall’insieme tramite l’operazione discard. Se si tenta di togliere dall’insieme un

valore che non è presente, il programma non restituisce errore ma è comunque bene controllare prima se l’elemento da manovrare è presente nell’insieme. Sugli insiemi si possono fare le operazioni proprie dell’insieme matematico:

  • Unione: si fa tramite |
  • Intersezione: si fa tramite & Per creare un insieme vuoto non si usano le parentesi graffe vuote ma si usa set(). I DIZIONARI sono delle coppie chiave-valore. I valori che vengono inseriti all’interno nel dizionario sono associati a delle chiavi che servono per ritrovare questi valori. Le chiavi hanno quasi la stessa funzione degli indici ma questi ultimi hanno dei valori numerici consecutivi che partono da 0, le chiavi possono essere degli elementi immutabili di qualsiasi tipologia, solitamente sono delle stringhe (non è però necessario). Ciò che si crea con i dizionari è una struttura simile a quella delle rubriche telefoniche: la chiave è il nome della persona e il valore è il suo numero di telefono. La chiave viene separata dal valore tramite i due punti. Quando non si è sicuri che un dato elemento sia all’interno del dizionario, si utilizza il metodo get che restituisce il valore del valore se la chiave è presente oppure restituisce None nel caso in cui non sia presente, non fa scoppiare il programma. Il metodo keys permette, se chiamato, di restituire tutte le chiavi del dizionario; il metodo values consente, se chiamato, di restituire tutti i valori del dizionario. Il metodo items invece restituiste le coppie chiave-valore come delle tuple. Nei dizionari è possibile anche specificare un valore di default che permette, nel caso in cui la chiave chiamata non sia nel dizionario, di restituire quel valore. La LIST COMPREHENSION è un modo conciso per creare una lista rielaborando una data sequenza. Tutte le operazioni che generalmente verrebbero scritte in tre righe di codice vengono compattate all’interno di una sola lista. L’utilizzo delle list comprehension fa in modo di avere un codice più leggibile e corto. ESEMPIO: squares = [x ** 2 for x in range(5)]. Questa dicitura prevede di creare una lista vuota e poi, tramite un ciclo for, per 5 volte, si prendono i valori e ne si fa il quadrato. I valori ottenuti sono 0^2 = 0, 1^2 = 1, 2^2 = 4, 32 = 9 e 4^2 = 16. Al posto di scrivere la lista vuota ed il ciclo for si compatta tutto in una sola riga. 19.11. L’operazione ZIP permette di accoppiare valori di sequenze diverse, come risultato si ottengono delle coppie dove i valori delle coppie sono i valori delle liste di partenza che sono stati messi insieme in una tupla. zip(sequence1, sequence2) non produce direttamente una sequenza di coppie ma genera una sequenza lazy di coppie ovvero i risultati non vengono calcolati subito ma soltanto quando sono necessari. È possibile usare un ciclo for con due variabili per stampare le coppie della funzione zip. Se è necessaria una lista è possibile crearne una con dentro i valori di zip, è una lista di coppie. L’operazione zip permette di lavorare contemporaneamente su due liste mettendo insieme i valori ma l’accoppiamento si ferma alla lista più breve: nell’esempio, 4pc non si accoppia perché la lista più breve è groceries con soli tre elementi. A volte è utile tenere conto dell’elemento della lista ma anche del suo indice associato. L’operazione ENUMERATE è un caso particolare di zip. Si può fare la zip tra un range ed una lista associando così un indice cresce ai valori della lista. enumerate(sequence) permette di ottenere le coppie degli elementi con i loro indici associati, è come uno zip con un range crescente. Anche enumerate genera una sequenza lazy di coppie quindi i risultati non vengono calcolati