Esercizio 4riferimenti, copie, None e indici
In questa pagina 8
Testo (questionari di novembre e dicembre 2022 e test d'esame di gennaio 2014 del corso di Fondamenti di Informatica UniPD; adattato da un tema d'esame in Java, dove gli errori sono NullPointerException e ArrayIndexOutOfBoundsException). Per ciascun frammento dire che errore segnala (o che cosa stampa) e perché. Teoria: Variabili, oggetti e riferimentiIn Python una variabile è un nome legato a un oggetto; tipo, identità e valore; assegnamento, aliasing, oggetti mutabili e immutabili, None.Variabili, oggetti e riferimenti →, Liste in Pythonlist come sequenza mutabile (array dinamico); indici, slicing e assegnamento a fette; metodi e loro costo; aliasing, copia superficiale e profonda; list comprehension; liste annidate e matrici.Liste in Python →, Funzioni in Pythondef e return, parametri posizionali, con nome e con valore predefinito, *args e **kwargs; passaggio per riferimento a oggetto; ambito delle variabili LEGB; docstring, type hint, lambda.Funzioni in Python →.
class Complessa:
def __init__(self):
self.reale = 0
self.imm = 0
# (a)
v = [None] * 10
for i in range(10):
v[i].reale = 0
# (b)
w = [0] * 10
for i in range(len(w) + 1):
print(w[i])
# (c)
a = [1, 2, 3]
b = a
b.append(4)
print(a)
# (d)
m = [[0] * 3] * 3
m[0][0] = 7
print(m)
# (e)
def aggiungi(l):
l.append(1)
def riassegna(l):
l = [9]
q = []
aggiungi(q)
riassegna(q)
print(q)
# (f)
def h(x=[]):
x.append(1)
return x
print(h(), h())(a) Lista di None
In Python [None] * 10 crea una lista con 10 riferimenti a None, non 10 oggetti Complessa (è l'analogo del new ComplexNumber[10] in Java, le cui celle contengono null). v[0].reale = 0 cerca di assegnare un attributo a None: AttributeError: 'NoneType' object has no attribute 'reale' (in Java: NullPointerException). Gli oggetti vanno creati uno per cella:
v = [Complessa() for _ in range(10)](b) Un indice di troppo
range(len(w) + 1) produce ma gli indici validi sono : stampa dieci volte 0, poi w[10] solleva IndexError: list index out of range (in Java: ArrayIndexOutOfBoundsException). Il ciclo corretto è range(len(w)). In Python gli indici negativi sono validi (w[-1] è l'ultimo): l'errore compare solo fuori da .
(c) Alias
b = a non copia la lista: a e b sono due nomi per lo stesso oggetto (b is a è True). b.append(4) modifica l'oggetto, quindi print(a) stampa [1, 2, 3, 4]. Per una copia indipendente: a[:], list(a) o a.copy(); c = a[:] dà un oggetto diverso ma uguale (c is a falso, c == a vero). Per le stringhe, che sono immutabili, il problema non c'è: s[0:len(s)] è uguale a s ma non si può modificare né l'una né l'altra.
(d) Righe condivise
[[0] * 3] * 3 ripete lo stesso riferimento a una sola lista interna tre volte: m[0][0] = 7 modifica quella lista e quindi tutte e tre le righe: stampa [[7, 0, 0], [7, 0, 0], [7, 0, 0]]. Per una matrice di righe indipendenti: [[0] * 3 for _ in range(3)] (stampa [[7, 0, 0], [0, 0, 0], [0, 0, 0]]). Le copie "superficiali" (copy, [:]) duplicano solo il livello esterno: per copiare anche le liste interne serve copy.deepcopy.
(e) Modificare o riassegnare il parametro
aggiungi(q) passa il riferimento alla lista (passaggio per assegnazione): l.append(1) modifica l'oggetto di q. riassegna(q) associa invece il nome locale l a una nuova lista [9]: q non cambia. Stampa [1]. Vale la regola: una funzione può modificare un oggetto mutabile ricevuto, non può far puntare a un altro oggetto il nome del chiamante.
(f) Valore di default mutabile
Il valore di default [] viene creato una sola volta, quando si esegue def, e poi riusato a ogni chiamata senza argomento: h() e h() restituiscono la stessa lista, che dopo le due chiamate vale [1, 1], quindi print(h(), h()) stampa [1, 1] [1, 1]. La forma corretta usa None:
def h(x=None):
if x is None:
x = []
x.append(1)
return xVerifica
v = [None] * 3
try:
for i in range(3):
v[i].reale = 0
except AttributeError as e:
print("AttributeError:", e)
w = [0] * 3
try:
for i in range(len(w) + 1):
print(w[i], end=" ")
except IndexError as e:
print("\nIndexError:", e)
print(w[-1] == w[len(w) - 1]) # True
a = [1, 2, 3]; b = a; c = a[:]
b.append(4)
print(a, b is a, c, c is a, c == [1, 2, 3]) # [1, 2, 3, 4] True [1, 2, 3] False True
m = [[0] * 3] * 3; m[0][0] = 7
print(m) # [[7, 0, 0], [7, 0, 0], [7, 0, 0]]
m = [[0] * 3 for _ in range(3)]; m[0][0] = 7
print(m) # [[7, 0, 0], [0, 0, 0], [0, 0, 0]]
def aggiungi(l): l.append(1)
def riassegna(l): l = [9]
q = []; aggiungi(q); riassegna(q)
print(q) # [1]
def h(x=[]):
x.append(1)
return x
print(h(), h()) # [1, 1] [1, 1]Errori comuni
- Credere che
[None] * no[Classe] * ncreinonoggetti. range(len(v) + 1)orange(1, len(v))(salta il primo): errori di uno.- Usare
b = aper copiare, o[[0] * c] * rper una matrice. - Aspettarsi che
l = ...dentro la funzione cambi la lista del chiamante. - Valori di default mutabili (
[],{}).
Versione ripasso
Questionari di novembre e dicembre 2022 e test di gennaio 2014 (UniPD, in Java: NullPointerException, ArrayIndexOutOfBoundsException). Teoria: Variabili, oggetti e riferimentiIn Python una variabile è un nome legato a un oggetto; tipo, identità e valore; assegnamento, aliasing, oggetti mutabili e immutabili, None.Variabili, oggetti e riferimenti →, Liste in Pythonlist come sequenza mutabile (array dinamico); indici, slicing e assegnamento a fette; metodi e loro costo; aliasing, copia superficiale e profonda; list comprehension; liste annidate e matrici.Liste in Python →.
[None] * 10→ 10 riferimenti aNone:v[i].reale = 0sollevaAttributeError(gli oggetti vanno creati:[Complessa() for _ in range(10)]).range(len(w) + 1)→IndexErrorall'ultima iterazione (indici validi ).b = aè un alias (b is a):b.append(4)cambia anchea. Copia:a[:],list(a),a.copy()(superficiale),copy.deepcopyper liste di liste.[[0] * 3] * 3: tre volte la stessa riga;[[0] * 3 for _ in range(3)]per righe indipendenti.- Passaggio per assegnazione:
l.append(1)modifica l'oggetto del chiamante,l = [9]no. - Default mutabile
def h(x=[]): creato una volta, condiviso tra le chiamate; si usax=None.
Errori comuni: [Classe] * n creduto oggetti; errori di uno; b = a per copiare; l = ... creduto visibile al chiamante; default mutabili.