

























































Studia grazie alle numerose risorse presenti su Docsity
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Prepara i tuoi esami
Studia grazie alle numerose risorse presenti su Docsity
Prepara i tuoi esami con i documenti condivisi da studenti come te su Docsity
Trova i documenti specifici per gli esami della tua università
Preparati con lezioni e prove svolte basate sui programmi universitari!
Rispondi a reali domande d’esame e scopri la tua preparazione
Riassumi i tuoi documenti, fagli domande, convertili in quiz e mappe concettuali
Studia con prove svolte, tesine e consigli utili
Togliti ogni dubbio leggendo le risposte alle domande fatte da altri studenti come te
Esplora i documenti più scaricati per gli argomenti di studio più popolari
Ottieni i punti per scaricare
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Appunti per preparare l' esame di informatica, del corso di laurea triennale ingegneria meccanica presso l'università di Firenze, anno accademico 2024/2025, integrati con esempi e teoria
Tipologia: Appunti
1 / 65
Questa pagina non è visibile nell’anteprima
Non perderti parti importanti!


























































Inizio lezione 14 aprile 2025
Una struttura dati è una strategia per organizzare dati e accedervi anche per modificarli in maniera efficiente. Per ora abbiamo introdotto le liste, oggetti che memorizzano sequenze di altri oggetti. Strutture dati sono definite dall’organizzazione in memoria, da cui derivano differenti proprietà riguardo all’efficienza e alla possibilità di effettuare o meno alcune operazioni. Si distingue spesso tra strutture lineari e nonlineari: ● organizzate in una sequenza di elementi (c’è un precedente e un successore); ● non organizzate in una sequenza di elementi: Una struttura dati è una strategia per organizzare dati e accedervi in modo efficiente. Le strutture dati sono definite dall'organizzazione in memoria, da cui derivano differenti proprietà riguardo all'efficienza e alla possibilità di effettuare alcune operazioni. Esistono molte strutture dati, come gli array, le liste, gli alberi, le code (queue) e le pile (stack). La scelta della struttura dati giusta dipende dalle specifiche esigenze del problema che si vuole risolvere e dalle operazioni che si devono effettuare sui dati Utilizzare la struttura dati corretta può portare a notevoli miglioramenti in termini di efficacia e prestazioni nell'elaborazione dei dati. Dall’insieme di operazioni che si possono eseguire su di esse, tipicamente: ● Inserimento ● Cancellazione ● Visita ● Ricerca Alcune strutture dati mantengono, strutturalmente, delle proprietà dei dati come ad esempio l’ordinamento (i.e., l’ordine alfabetico oppure ordine crescente numerico). Strutture dati mantengono proprietà strutturali dei dati: Ordinamento: alcune strutture dati mantengono i loro elementi in ordine, in modo che possano essere facilmente cercati o ordinati. ● Ad esempio, liste e array hanno un ordinamento sequenziale, ma possono anche essere ordinati lessicograficamente. ● Mentre gli alberi binari di ricerca mantengono gli elementi ordinati in un albero Unicità: alcune strutture dati consentono l'inserimento di un elemento solo se non è già presente nella struttura. Ad esempio, gli insiemi (set). Indicizzazione: alcune strutture dati consentono l'accesso ai loro elementi utilizzando un indice numerico. Ad esempio, gli array e le liste sono indicizzati utilizzando un numero intero che indica la posizione dell'elemento nell'array o nella lista. Gerarchia: alcune strutture dati organizzano gli elementi in modo gerarchico, come ad esempio gli alberi. ● Ogni elemento può avere uno o più elementi figli; ● possono essere utilizzati per rappresentare relazioni di parentela o dipendenze tra i dati; Priorità: alcune strutture dati organizzano gli elementi in base alla loro priorità. ● Ad esempio, le code con priorità mantengono gli elementi in ordine in base alla loro importanza; ● possono essere utilizzate per gestire attività in attesa di esecuzione; In generale, la scelta della struttura dati giusta dipende dalle specifiche esigenze del problema che si vuole risolvere e dalle operazioni che si devono effettuare sui dati.
STRINGHE Le stringhe sono oggetti con struttura interna che rappresentano sequenze di caratteri. Le stringhe hanno le proprietà di array e liste con in più l’immutabilità. Le stringhe sono immutabili quindi una modifica di una stringa è possibile solo per riassegnamento. La suddivisione (slicing) di una stringa può essere effettuata utilizzando la sintassi [start:end], dove start e end sono gli indici del primo e dell'ultimo carattere della sottostringa da estrarre. La suddivisione di una stringa non modifica la stringa originale, ma crea una nuova stringa.
Inizio lezione 14 aprile 2025
è necessario crearne una nuova ES. S= ‘ciao a tutti’ S=S[:4] Print (S) —> ciao Ci sono moltissimi metodi per trasformare una stringa… e tutti i metodi restituiscono una nuova stringa. Le stringhe possono ovviamente anche essere indicizzate e suddivise come le liste molto flessibili quando si lavora con il testo Ad esempio capitalize() rende la stringa con l’iniziale maiuscola S= ‘ciao a tutti’ S=S.capitalize() Print (S) —> ciao a tutti Il metodo capitalize() viene applicato alla stringa originale s, che rende maiuscola la prima lettera della stringa e converte tutte le altre lettere in minuscolo creata nuova stringa assegnata s, sovrascrivendo la precedente upper() converte tutte le lettere della stringa in maiuscolo lower() converte tutte le lettere della stringa in minuscolo strip() rimuove gli spazi bianchi all'inizio e alla fine della stringa replace(old, new) sostituisce tutte le occorrenze di una sottostringa con un'altra sottostringa split(separator) suddivide la stringa in una lista di sottostringhe, utilizzando il separatore specificato Ricerca in una stringa Posso cercare occorrenze esatte di sottostringhe con in Es. S= ‘ciao a tutti’ If ‘tutti’ in S: Print (“stiamo tutti salutando”) L'operatore in viene utilizzato per cercare la sottostringa "tutti" all'interno della stringa s. Poiché la sottostringa è presente nella stringa, viene stampato il messaggio «stiamo salutando tutti» Python offre altri metodi per la ricerca di sottostringhe, come ad esempio: find(substring): restituisce l'indice della prima occorrenza della sottostringa, o -1 se la sottostringa non è presente nella stringa. rfind(substring): restituisce l'indice dell'ultima occorrenza della sottostringa, o -1 se la sottostringa non è presente nella stringa (reverse find… search from the right) count(substring): restituisce il numero di occorrenze della sottostringa all'interno della stringa. S= ‘ciao a tutti’ Print(‘tutti ’ in S) Print(S.find(‘ao’)) Print(S.rfind(‘tu’)) Print(s.count(‘t’)) Trasformazione di stringa → lista A volte è conveniente passare ad una struttura mutabile per manipolare più facilmente la stringa S= ‘ciao a tutti’ s_l=list(s) Print(s_l) Una volta manipolata la lista evitando il ri-assegnamento ad ogni modifica…
… si fa la trasformazione inversa: da lista → stringa E’ poi ovviamente conveniente ri-ottenere una stringa… S=’ ’.join(S_l)
Inizio lezione 14 aprile 2025
Possiamo però voler inserire ad esempio in testa alla lista (o in una qualunque altra posizione) Es. L.insert (0 , 0) Print (L) ->[0,1,2,3,4]
Il metodo insert prende due parametri l’indice in cui voglio posizionare il nuovo elemento e il valore da inserire… L’elemento precedente e tutti gli altri sono spostati a destra «Relativamente» efficiente → tempo lineare, poiché tutti gli elementi devono essere spostati Inserimento ordinato Lo specifico insert(i,value) ci permette di inserire gli elementi mantenendo l’ordinamento dei valori della lista Def ordered_insert (l, value): i= While i < len (l) and l[i] < value : i += 1 l.insert(i, value)
l=[1, 2, 10] ordered_insert(l, 3) Print(l)-> [1, 2, 3, 10] Esempio d’uso di insert: Inserimento ordinato Lo specifico insert(i,value) ci permette di inserire gli elementi mantenendo l’ordinamento dei valori della lista Def ordered_insert(l, value ): i = 0 While i < len(l) and l[i]: i += 1 l.insert(i, value)
l = [1, 2, 10] ordered_insert(l, 3 ) Print (l) -> [1, 2, 3, 10]
L’utilizzo dell'inserimento ordinato richiede lista già ordinata. Se non è ordinata, l'inserimento ordinato non funzionerà correttamente possibile ordinare la lista con sort() ➢ Esempio, my_list.sort() Nota: l'ordinamento di una lista richiede un tempo più che proporzionale al numero degli elementi n ➢ vedremo meglio essere: O(nlogn) Trovare la posizione corretta in cui inserire il nuovo elemento richiede ulteriore «tempo lineare» Cancellazione Oltre ad inserire valori in una lista può risultare conveniente eliminarli. L= [1, 2, 3]
Inizio lezione 14 aprile 2025
l.pop(0) Print(L) Il metodo pop estrae (elimina) e restituisce il valore all’indice index. Gli elementi successivi sono spostati a sinistra. Anche qui tempo lineare per lo spostamento Senza specificare l'indice, il metodo pop() rimuove e restituisce l'ultimo elemento della lista. Ad esempio, my_list.pop() rimuove e restituisce l'ultimo elemento di my_list La cancellazione in generale richiede un tempo lineare O(n), tutti gli elementi successivi al rimosso devono essere spostati di una posizione per occupare il suo posto. La rimozione di elementi in una lista può causare una riduzione delle prestazioni (dovuta a riallocazione della memoria per la lista). Se si deve rimuovere un grande numero di elementi… → può essere più efficiente creare una nuova lista contenente solo gli elementi rimanenti (evita la frammentazione) Cancellazione per valore Possiamo eliminare elementi ricercandoli per valore. Il metodo remove elimina la prima occorrenza dell’oggetto cercato L=[‘a’, ‘b’, ‘c’] l.remuve(‘b’) Print(L) ->[‘a’, ‘c’] Gli elementi successivi sono spostati a sinistra. Se ci sono elementi duplicati occorre chiamare remove tante volte quanti sono i duplicati. Comparazione dei Tempi di Cancellazione lista di 100 milioni dielementi Misuriamo il tempo per cancellare il primo e l'ultimoelemento… Comparazione: pop(0), pop() Analogo per insert
Visita : L’accesso alla lista avviene per indice o tramite iterazione L= [1, 2, 3] For el in l: Print(el)
La visita (o attraversamento) con la funzione predefinita enumerate(), restituisce una tupla contenente l'indice dell'elemento e l'elemento stesso Ad esempio, ● for i, x in enumerate(my_list): ● itera su tutti gli elementi di my_list, restituendo l'indice dell'elemento in i e l'elemento stesso in x L= [1, 2, 3] For i ,el in enumerate(l): Print(i, el)
Esempio rimuovere elementi in L2 da L1: La funzione remove_dups prende due liste come input, L1 e L2 All'interno di un loop for, la funzione verifica se
Inizio lezione 14 aprile 2025
Img = [0]480 i, j= 30, 20 Img[640 * i + j] Per matrici multidimensionali ad esempio a tre dimensioni un inizializzazione a zero ha la seguente forma: Mat = [0]IJK L’accesso all’elemento con indici i, j, k: Mat[ iJK + jK + k ] Svantaggio: necessario mantenere delle variabili con le dimensioni interne: I, J and K Esistono librerie avanzate per la manipolazione di array e tensori, con le principali funzionalità di algebra lineare (e.g. numpy) Quindi, scriviamo semplici funzioni su matrici senza l'uso di librerie Python esistenti che ci consentano di usare la notazione: Mat[i][j] Esempio funzione che inizializza una matrice di zeri di dimensioni rows, cols: Def zeros_matrix(rows, cols): Matrix=[] For i in range (row): matrix.append([0]cols) Return Matrix Questo: matrix = [[0]cols]*rows ,non crea una matrice
Attraverso l’inizializzazione di una matrice di zeri si può facilmente scrivere la funzione per inizializzare la matrice identità. Inserendo 1 negli elementi della diagonale principale In sintesi, la funzione zeros_matrix crea e restituisce una matrice di dimensioni specificate, inizializzata con tutti gli elementi a
Funzione per la somma di due matrici : La funzione verifica se le dimensioni delle matrici A e B sono uguali. Se le dimensioni non sono uguali, la funzione restituisce None per indicare un errore. Facilmente estendibile a: sottrazione, moltiplicazione e divisione per elementi
Inizio lezione 14 aprile 2025
Nel campo dell’informatica, i linguaggi di programmazione rappresentano uno strumento essenziale per comunicare con i computer e per istruire le macchine nell’esecuzione di compiti specifici. Un linguaggio di programmazione è, in sostanza, un insieme ben definito di regole sintattiche e semantiche che permettono a un essere umano di scrivere istruzioni comprensibili e processabili da una macchina. I linguaggi si suddividono principalmente in due grandi categorie: quelli di basso livello e quelli di alto livello.I linguaggi di basso livello, come il linguaggio macchina e l’assembly, sono molto vicini all’architettura fisica dell’elaboratore e permettono un controllo diretto dell’hardware. Tuttavia, proprio per questa loro vicinanza al linguaggio nativo del processore, risultano estremamente complessi e poco leggibili per un programmatore umano. Al contrario, i linguaggi di alto livello, come Python, Java o C++, sono progettati per essere intuitivi, leggibili e vicini al modo naturale di ragionare delle persone. Questi ultimi, però, non possono essere eseguiti direttamente dal computer e devono essere convertiti in linguaggio macchina tramite un processo di compilazione o interpretazione. Alcuni linguaggi, come C o C++, utilizzano compilatori che traducono tutto il codice sorgente in un file eseguibile prima che venga eseguito. Altri, come Python, si appoggiano su interpreti che traducono ed eseguono il codice riga per riga. Un esempio particolare è Java, che prima compila il codice in un formato intermedio chiamato bytecode, poi lo interpreta attraverso una macchina virtuale chiamata JVM, rendendo il programma portabile su diverse piattaforme.Un programma informatico è composto da una struttura ben definita che comprende dichiarazioni, istruzioni ed elementi di controllo del flusso. Le dichiarazioni sono necessarie per definire le variabili e le costanti che verranno utilizzate nel programma. Le istruzioni specificano le operazioni da eseguire, mentre i costrutti di controllo permettono di determinare l’ordine di esecuzione delle operazioni, modificandolo in base a condizioni logiche o ripetizioni.
Le variabili rappresentano delle entità fondamentali in ogni linguaggio di programmazione. Si tratta, in sostanza, di porzioni di memoria identificate da un nome, che possono contenere valori modificabili nel tempo. Per esempio, si può dichiarare una
Inizio lezione 14 aprile 2025
Un array è una sequenza di elementi tutti dello stesso tipo, memorizzati in posizioni contigue di memoria e accessibili tramite un indice numerico. Questo significa che se si vuole conservare i voti di uno studente, si potrebbe utilizzare un array di numeri, in cui ogni elemento rappresenta un voto specifico. L’uso dell’indice permette di accedere rapidamente agli elementi, ma allo stesso tempo impone che la dimensione dell’array sia nota o gestita con cura.
Rispetto agli array, le liste sono più flessibili perché permettono di aggiungere o rimuovere elementi anche in posizioni intermedie, e la loro dimensione può crescere dinamicamente. Le pile e le code, invece, sono strutture dati che seguono regole precise di accesso: nel caso delle pile si adotta la logica LIFO (Last In, First Out), mentre per le code si applica il principio FIFO (First In, First Out). Queste strutture sono molto usate, per esempio, nella gestione delle chiamate di funzione (pila) o nelle operazioni di stampa (coda).
Oltre a conoscere le strutture dati, è essenziale imparare a scrivere algoritmi che le manipolano in modo corretto ed efficiente. Questo porta alla progettazione di algoritmi che effettuano operazioni come l’inserimento, la cancellazione, la ricerca e l’ordinamento dei dati. Uno dei compiti più ricorrenti è, ad esempio, ordinare un array di numeri in ordine crescente. Esistono numerosi algoritmi per farlo, come il bubble sort, il selection sort o il quicksort, ognuno con caratteristiche diverse in termini di semplicità e velocità.
Nel mondo reale, i dati e gli algoritmi sono alla base di tutte le applicazioni informatiche: dai gestionali alle app mobili, dai videogiochi ai sistemi bancari. Per questo motivo, comprendere il modo in cui i dati vengono elaborati dal programma è un passo fondamentale per scrivere software robusto e performante.
Con l’aumentare della complessità di un programma, diventa anche necessario adottare un approccio metodico allo sviluppo, introducendo il concetto di modularità. Un programma modulare è suddiviso in sottoprogrammi o moduli indipendenti, ognuno con un compito preciso. Questo approccio facilita la manutenzione del codice, ne migliora la leggibilità e permette il riuso di componenti in progetti diversi. Ogni modulo può essere testato in modo indipendente, permettendo così una più facile individuazione degli errori.
Inoltre, l’organizzazione modulare consente una migliore collaborazione nei progetti di gruppo, dove più sviluppatori possono lavorare contemporaneamente su moduli diversi. Quando si lavora in team, è importante anche adottare convenzioni di scrittura del codice e documentazione, per facilitare la comprensione reciproca e la gestione a lungo termine del software.
Un altro aspetto essenziale nell’apprendimento dell’informatica è la logica computazionale, ovvero la capacità di scomporre un problema in sotto-problemi elementari, individuare le condizioni e le ripetizioni necessarie per risolverlo, e costruire una sequenza ordinata e corretta di istruzioni. Questa abilità si sviluppa con l'esercizio e l'analisi di esempi concreti, come la creazione di un programma che calcoli il massimo tra tre numeri, o la simulazione di un bancomat.
In conclusione, lo studio dei fondamenti dell’informatica non si limita alla semplice memorizzazione di comandi e costrutti di un linguaggio di programmazione. Al contrario, richiede un approccio logico e analitico, una buona comprensione dei concetti alla base dell’elaborazione dei dati e un uso consapevole degli strumenti messi a disposizione dalla programmazione. Solo padroneggiando questi principi sarà possibile affrontare problemi più complessi e costruire soluzioni software efficaci, affidabili e ben progettate.
1. Dichiarazione e uso di variabili
In un linguaggio come Python, si può dichiarare una variabile senza specificarne il tipo (a differenza di C o Java):
Inizio lezione 14 aprile 2025
a = 5 b = 3 somma = a + b print("La somma è:", somma) Spiegazione : Qui a e b sono variabili che contengono numeri interi. L’operazione a + b restituisce 8, che viene assegnato alla variabile somma e poi stampato.
2. Condizione con if-else
Verifica della maggiore età:
eta = int(input("Inserisci la tua età: ")) if eta >= 18: print("Sei maggiorenne.") else: print("Sei minorenne.") Spiegazione : L’utente inserisce un numero che viene interpretato come età. Se l’età è almeno 18, il programma stampa che è maggiorenne, altrimenti dice che è minorenne.
3. Ciclo for: somma dei primi N numeri
N = int(input("Inserisci un numero N: ")) somma = 0 for i in range(1, N+1): somma += i print("La somma dei primi", N, "numeri è:", somma) Spiegazione : Il ciclo for parte da 1 e arriva a N (incluso). Ogni numero viene sommato a somma, ottenendo alla fine la somma totale dei numeri da 1 a N.
4. Funzione per calcolare il quadrato di un numero
def quadrato(x): return x * x Inizio lezione 14 aprile 2025 **1. Matrici Sparse (Dizionari)** Le matrici sparse sono matrici in cui la maggior parte degli elementi è pari a zero. Per risparmiare memoria e ottimizzare i calcoli, si memorizzano solo i valori non nulli utilizzando dizionari. Principali operazioni: Conversione da lista a matrice sparsa: * Si parte da una lista di liste. * Si crea un dizionario vuoto. * Per ogni elemento diverso da zero, si aggiunge la coppia di indici come chiave e il valore nel dizionario. Moltiplicazione di matrici sparse: * Si inizializza un dizionario vuoto per il risultato. * Si itera sugli elementi della prima matrice e si verifica la presenza nella seconda. * Se esistono in entrambe, si calcola il prodotto e lo si aggiunge al risultato. Somma tra matrici sparse: * Si copia la prima matrice nel risultato. * Si aggiungono gli elementi della seconda, aggiornando i valori se la chiave già esiste. Stampa della matrice sparsa: * Due cicli annidati sulle dimensioni della matrice. * Si verifica la presenza dell'elemento e lo si stampa, altrimenti si stampa uno zero. Esempio di Codice: ```python def list_to_sparse(matrix): sparse = {} for i, row in enumerate(matrix): for j, val in enumerate(row): if val != 0: sparse[(i, j)] = val sparse['_dim'] = (len(matrix), len(matrix[0])) # Dimensioni return sparse 2. Set (Insiemi)
I set in Python sono collezioni di elementi unicinon ordinati.
Principali operazioni:
Creazione :Utilizzo della funzione set() per creare insiemi. Inserimento :
add() per un singolo elemento.update() per più elementi. Rimozione:remove() solleva un errore se l'elemento non esiste.Inizio lezione 14 aprile 2025
discard() non genera errori.pop() rimuove un elemento casuale.clear() svuota l'insieme. Operazioni insiemistiche :| o union()):Contiene tutti gli elementi di entrambi gli insiemi.& o intersection()):Contiene solo gli elementi comuni.- o difference()): Elementi presenti nel primo insieme ma non nel secondo.^ o symmetric_difference()):Elementi presenti solo in uno dei due insiemi.Esempio di Codice:
a = {1, 2, 3} b = {3, 4, 5} # Unione union_set = a | b # Intersezione intersection_set = a & b # Differenza difference_set = a - b print("Unione:", union_set) print("Intersezione:", intersection_set) print("Differenza:", difference_set) Approfondimento con Esempi Esempio 1: Moltiplicazione di Matrici Sparse
def multiply_sparse(mat1, mat2): result = {} for (i, j), val in mat1.items(): if (i, j) in mat2: result[(i, j)] = val * mat2[(i, j)] return result Esempio di utilizzo mat1 = {(0, 1): 3, (2, 3): 5, '_dim': (3, 4)} mat2 = {(0, 1): 2, (2, 3): 4, '_dim': (3, 4)} print("Prodotto:", multiply_sparse(mat1, mat2)) Output:
Inizio lezione 14 aprile 2025
dict2 = {'apple': 1.6, 'orange': 0.9} print("Dizionari Fusi:", merge_dictionaries(dict1, dict2))
Output: Dizionari Fusi: {'apple': [1.5, 1.6], 'banana': [0.8, None], 'orange': [None, 0.9]}
Il documento evidenzia come l'uso di dizionari e set in Python permetta di gestire in modo efficiente matrici sparse e collezioni uniche. La comprensione di queste strutture dati è fondamentale per ottimizzare algoritmi che richiedono operazioni matematiche complesse o gestione di dati non duplicati. Il documento tratta i concetti base della programmazione in Python, con un focus su **tipi di dato** , **bl duck typing** , e programmazione orientata agli oggetti (OOP).Ecco i punti principali: **Tipi di Dato: Scalari e Strutturati** Un **tipo di dato** definisce il tipo di informazioni che una variabile può rappresentare e le operazioni che si possono eseguire su di essa. **Tipi scalari** :`int`, `float`, `bool` **Tipi strutturati** :`str`, `list`, `dict`, `set`, `tuple` Alcuni operatori (+, -) e funzioni (`max`, `min`, `len`, `for`) possono essere applicati a più tipi di dati. Esempio: ```python x = 5 # int (scalare) y = [1, 2, 3] # list (strutturato) print(max(y)) # Output: 3 Duck Typing in Python
Il duck typing è un approccio in cui un oggetto viene trattato in base ai metodi/attributi che possiede piuttosto che alla sua classe. Questo concetto deriva dal proverbio: ● "If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck." ● Python sfrutta questo principio, consentendo alle funzioni di lavorare con qualsiasi oggetto che supporti un certo comportamento , indipendentemente dal tipo.
Esempio:
def somma_massimi(c1, c2): Inizio lezione 14 aprile 2025 return max(c1) + max(c2) print(somma_massimi([1, 2, 3], (4, 5, 6))) # Output: 9 In questo esempio, la funzione accetta liste o tuple poiché entrambe supportano l’operatore max.
Classi in Python: Introduzione e Struttura
Python supporta la programmazione orientata agli oggetti (OOP) tramite classi. Le classi permettono di creare nuovi tipi di dato definiti dall'utente con attributi (proprietà) e metodi(funzioni associate). Il metodo speciale __init__ è il costruttore e viene chiamato alla creazione di un oggetto. Gli attributi si riferiscono all'istanza corrente tramite il parametro self.
Esempio:
class Studente: def __init__(self, nome): self.nome = nome self.voti = [] def aggiungi_voto(self, voto): self.voti.append(voto) def media(self): return sum(self.voti) / len(self.voti) if self.voti else 0 stud = Studente("Mario") stud.aggiungi_voto(30) stud.aggiungi_voto(28) print(stud.media()) # Output: 29. Esempio di Classe: Matrice
Il documento propone un esempio di classe Matrix, che rappresenta una matrice come una lista di liste. Implementa metodi come:
● __str__: per la stampa personalizzata. ● shape: per ottenere la dimensione della matrice.
Esempio:
class Matrix: Inizio lezione 14 aprile 2025 ```python data = { "nome": "Anna", # str "età": 25, # int "voti": [28, 30, 27], # list "is_graduated": True # bool } # Calcolo della media dei voti media = sum(data["voti"]) / len(data["voti"]) print(f"{data['nome']} ha una media voti di {media:.2f}") Consiglio Pratico: Utilizza i dizionari quando hai bisogno di associare informazioni eterogenee a una singola entità (es. dati di uno studente). 🦆 2. Duck Typing: Flessibilità negli Oggetti
Il duck typing è una caratteristica che rende Python estremamente flessibile. Se un oggetto si comporta come un altro tipo, Python lo tratta come tale.
💡 Esempio di Duck Typing:
def somma_elementi(c): # Funziona con qualsiasi oggetto iterabile return sum(c) print(somma_elementi([1, 2, 3])) # Lista print(somma_elementi((4, 5, 6))) # Tupla print(somma_elementi({7, 8, 9})) # Set 🔑 Vantaggi:
● Flessibilità: Non importa il tipo, purché l'oggetto si comporti come richiesto. ● Adattabilità: Le funzioni sono più generiche e riutilizzabili.
🚩 Attenzione:
● Python non verifica il tipo degli oggetti al momento della dichiarazione, quindi l’errore si verifica solo se l’oggetto non supporta l’operazione richiesta. ● Per evitare crash, puoi usare la funzione isinstance() per verificare i tipi.
🏛3. Programmazione Orientata agli Oggetti (OOP)
Inizio lezione 14 aprile 2025
La OOP consente di modellare concetti reali in modo strutturato, con classi che rappresentano entità e oggetti come istanze.
Concetti Chiave:
● Classe: Modello che definisce attributi e metodi. ● Oggetto: Istanza di una classe. ● Attributi: Variabili all’interno di una classe. ● Metodi:Funzioni definite all’interno della classe. ● Costruttore (__init__):Metodo speciale per l’inizializzazione. Esempio Avanzato:
Definiamo una classe per rappresentare un libro in una biblioteca.
class Libro: def __init__(self, titolo, autore, pagine): self.titolo = titolo self.autore = autore self.pagine = pagine def descrizione(self): return f"'{self.titolo}' di {self.autore}, {self.pagine} pagine" def is_lungo(self): return self.pagine > 300 libro = Libro("Il Signore degli Anelli", "Tolkien", 1178) print(libro.descrizione()) # Output descrittivo print(libro.is_lungo()) # Output: True 🔧 Buone Pratiche OOP:
__str__per creare una rappresentazione leggibile dell’oggetto.🔢 4. Esempio Avanzato: Classe Matrice Sparsa
Le matrici sparse sono utili per rappresentare dati con molti zeri, risparmiando memoria.
class SparseMatrix: def __init__(self): self.elements = {}