Quando si lavora con dati numerici in Python, la scelta veramente credibile è l’ndarray di NumPy. Questo oggetto generalizza la semplice lista in un contenitore multidimensionale omogeneo e veloce che sostiene quasi tutto lo stack del calcolo scientifico. In pratica userai soprattutto due ranghi: array monodimensionali (vettori) e bidimensionali (matrici). Ogni rango aggiunge un asse: un vettore possiede un solo asse, una matrice ne possiede due e lo schema si estende manualmente a tensori di ordine superiore.

Ogni array porta con sé una descrizione sintetica:

La creazione di array è banale, ma va fatta correttamente. Il modo più esplicito è wrappare una sequenza Python:

numpy.array([1, 2, 3], dytpe=numpy.float64)

L’argomento opzionale dtype garantisce storage deterministico e portabile. Copiare un array esistente è altrettanto diretto: y = numpy.array(x) produce una copia che può divergere in sicurezza dall’originale.

Per schemi d’inizializzazione ricorrenti nel codice reale, conviene usare le factory function di NumPy: sono più chiare e veloci dei loop manuali.

Costruttore Semantica (esempio)
numpy.zeros(shape, dtype) Array di zeri, ad es. numpy.zeros((2, 3))
numpy.ones(shape) Array di uni, ad es. numpy.ones(5)
numpy.arange([start,] stop[, step]) Intervallo half-open con passo opzionale, ad es. numpy.arange(0, 6, 2)[0, 2, 4]
numpy.eye(5) Matrice identità di ordine n
numpy.linspace(start, stop, num) num campioni equispaziati tra gli estremi, inclusi

Una volta ottenuti i dati, l’aritmetica elemento-per-elemento “funziona e basta”. Gli operatori infissi usuali (+, -, *, /) si applicano a forme compatibili producendo nuovi array:

x + y # somma punto-a-punto
x * y # prodotto punto-a-punto

Se intendi modificare in-place (tipico per array grandi dove le allocazioni pesano), aggiungi il segno di uguale (x += y, x *= y). Questo idioma è sia più veloce sia più frugale in memoria.

Per un autentico prodotto matriciale abbandona *. L’interfaccia canonica è numpy.dot(A, B); nelle versioni moderne di Python puoi preferire l’operatore infisso @ per leggibilità:

C = A @ B # equivalente a numpy.dot(A, B)

Qui va rispettato il contratto algebrico: il numero di colonne di A deve eguagliare il numero di righe di B. NumPy solleverà immediatamente un’eccezione in caso di dimensioni incompatibili: un fallimento precoce che evita ore di corruzione silente.

Gli array non sono bloccati nella loro forma originale. Con reshape puoi reinterpretare lo stesso buffer con una nuova geometria a patto che il numero totale di elementi resti invariato:

x.reshape((2, 3)) # da (3, 2) a (2, 3) senza copia

Poiché non avviene alcun riordinamento, l’operazione costa O(1): concettualmente potente e computazionalmente gratuita.