Una lista Python è un contenitore ordinato e modificabile che memorizza elementi di tipi variati (interi, stringhe, altre liste) in una sola variabile. Questa struttura si basa su un meccanismo di array dinamico di puntatori: la memoria allocata cresce a scaglioni ad ogni aggiunta, rendendo le operazioni comuni rapide senza configurazioni particolari. Comprendere questo funzionamento interno permette di scegliere meglio tra i diversi metodi disponibili per manipolare i propri dati quotidianamente.
Array dinamico e sovrallocazione: cosa succede in memoria
Quando uno script aggiunge elementi uno alla volta con append(), Python non riserva la memoria elemento per elemento. Il linguaggio prealloca una capacità superiore al numero di elementi effettivamente memorizzati, spesso per un fattore vicino al doppio. Questo meccanismo rende ogni chiamata a append() ammortizzata in tempo costante.
Il rovescio di questa strategia appare durante le riallocazioni. Quando la capacità preallocata viene raggiunta, Python copia l’intero array in un nuovo spazio di memoria più grande. Su uno script che raccoglie progressivamente righe di file o misurazioni, questi picchi di copia rimangono impercettibili per alcune migliaia di elementi, ma diventano misurabili su volumi più grandi.
Per questa ragione, raggruppare le aggiunte con extend() piuttosto che concatenare append() in un ciclo riduce il numero di riallocazioni. Se la dimensione finale è nota in anticipo, costruire la lista in un’unica volta (ad esempio tramite una comprensione) rimane il metodo più efficiente. Una guida python su Tech Mafia dettaglia diverse di queste strategie applicate a casi concreti di trattamento dei dati.

Comprensioni di lista Python: sintassi e ottimizzazione in 3.12
La comprensione di lista condensa un ciclo di trasformazione in un’unica riga. La sintassi di base segue questo schema:
risultato = [espressione per elemento in iterabile se condizione]
Questa scrittura sostituisce un blocco for classico seguito da un append(), e il guadagno non è solo cosmetico. In Python 3.12, il progetto Faster CPython ha eliminato la creazione di un frame interno separato per ogni comprensione. Il risultato: meno costi di gestione e un trattamento più veloce delle trasformazioni comuni come la pulizia delle stringhe o il filtraggio dei numeri in un set di dati.
Un esempio concreto: estrarre le età superiori a 30 da una lista di dizionari.
eta = [p["eta"] per p in persone se p["eta"] > 30]
Questa riga fa il lavoro di un ciclo for, di un test condizionale e di un append in un’unica espressione leggibile. La comprensione diventa meno adatta quando la logica interna supera due condizioni o implica effetti collaterali (scrittura in un file, chiamata di rete). In questo caso, tornare a un ciclo esplicito migliora la leggibilità del codice.
Slicing e indice negativo: accedere ai dati senza ciclo
Lo slicing permette di estrarre una sottolista senza scrivere un ciclo. La notazione utilizza tre parametri tra parentesi quadre: inizio, fine e passo.
lista[2:5]restituisce gli elementi agli indici 2, 3 e 4 (il limite finale è escluso)lista[::-1]inverte l’ordine dell’intera lista senza modificare l’originalelista[::2]seleziona un elemento su due, utile per campionare dati da sensori o log
L’indice negativo conta a partire dalla fine: lista[-1] dà l’ultimo elemento, lista[-3:] gli ultimi tre. Questa notazione evita di calcolare la lunghezza della lista con len() prima di accedere ai suoi elementi finali.
Lo slicing crea una copia superficiale della porzione estratta. Modificare la sottolista non modifica la lista originale, il che protegge i dati sorgente durante un trattamento esplorativo.
Ordinare, filtrare e combinare liste con le funzioni native
Python fornisce due approcci di ordinamento. Il metodo sort() modifica la lista in loco, mentre la funzione sorted() restituisce una nuova lista ordinata senza toccare l’originale. La distinzione è importante quando il programma deve mantenere l’ordine iniziale per un altro trattamento.
Il parametro key= accetta una funzione che definisce il criterio di ordinamento. Ordinare una lista di stringhe per lunghezza, ad esempio:
parole_ordinate = sorted(parole, key=len)
Per filtrare, la funzione filter() applica una condizione a ogni elemento e restituisce un iteratore. Combinata con list(), produce una nuova lista:
positivi = list(filter(lambda x: x > 0, numeri))
La combinazione di due liste avviene tramite l’operatore + (concatenazione) o tramite extend(). La differenza: + crea una nuova lista, extend() modifica la lista esistente. Su volumi consistenti, extend() consuma meno memoria poiché non duplica la struttura.
append(elemento)aggiunge un singolo elemento alla fine della listaextend(iterabile)aggiunge ogni elemento di un iterabile alla lista esistenteinsert(indice, elemento)posiziona un elemento in una posizione precisa, ma sposta tutti i successivi (costo proporzionale alla dimensione della lista)pop(indice)rimuove e restituisce l’elemento all’indice dato, o l’ultimo se non viene specificato alcun indice

Liste nidificate e strutture tabulari in Python
Una lista può contenere altre liste, formando una struttura bidimensionale comparabile a una tabella. Ogni sottolista rappresenta quindi una riga di dati:
tabella = [["Alice", 28], ["Bob", 35], ["Clara", 42]]
Accedere all’età di Bob si fa con tabella[1][1], ovvero la seconda riga, seconda colonna. Questo formato è adatto per piccoli set di dati (alcune decine di righe), ma mostra i suoi limiti oltre: ogni accesso tramite doppio indice rende il codice meno leggibile, e le operazioni colonna per colonna (somma, media) richiedono cicli espliciti.
Per trattamenti tabulari più strutturati, convertire queste liste nidificate in dizionari (con chiavi nominate) migliora la chiarezza. Ogni riga diventa un dizionario con chiavi esplicite come “nome” e “età”, il che rende il codice auto-documentato.
Le liste Python coprono la maggior parte delle esigenze di memorizzazione e trasformazione dei dati negli script quotidiani. La scelta tra comprensione, ciclo esplicito o funzione nativa dipende soprattutto dalla leggibilità ricercata e dal volume trattato. Quando i dati superano alcune centinaia di righe o richiedono operazioni colonne, la transizione verso strutture specializzate come i DataFrame di pandas diventa un prolungamento naturale del lavoro iniziato con le liste.



