Sulla list comprehension (domanda non inerente a HW)

Plush (340 points)
2 4 7
asked Nov 14, 2019 in Programmare in Python by Plush (340 points)
Se genero in memoria due liste:

a = [[4,2,8,6],[1,5,61,3]]

b = [[0,0,0,0],[0,0,0,0]])

poi, eseguo sulla console:

b[0][0] = a[0][0]

allora giustamente ritorna come lista:

b = [[4,0,0,0],[0,0,0,0]]

Tuttavia se la stessa lista b è generata tramite una list comprehension:

b = [[0]*len(a[0])]*len(a)

ed eseguo la stessa operazione di prima:

b[0][0] = a[0][0]

adesso ritorna come lista:

b = [[4,0,0,0],[4,0,0,0]]

Quindi mi chiedo, probabilmente quando una lista è generata sotto forma di list comprehension, allora l'indicizzazione funziona in modo diverso? Non trovo niente a riguardo, se qualcuno sa cosa sta succedendo potrebbe farmi sapere di più?
238 views

1 Answer

Best answer
E
Edward (25950 points)
2 4 172
answered Nov 14, 2019 by Edward (25,950 points)
selected Nov 14, 2019 by Plush
Praticamente quando usi * come operatore di ripetizione, quello ripete quell'identico oggetto,

Infatti se provi a fare lista = [[1]]*3 noterai che id(lista[0]) == id(lista[1]).

Qui entrano in gioco gli oggetti mutabili e gli oggetti immutabili.
Quando ripeti un oggetto mutabile, e vai a modificarlo (esempio: lista), siccome l'oggetto è identico per tutte le volte che viene ripetuto, allora ne vengono modificate anche le sue ripetizioni.
Questo non avviene con gli oggetti immutabili. Non potendoli modificare, tu in genere modifichi il contenitore dell'oggetto, e non l'oggetto stesso come avviene con quelli mutabili.

EDIT:

Comunque [[0]*len(a[0])]*len(a) non è una list comprehension.
Una list comprehension è del tipo:

[x**2 for x in lista]
[i for i in range(num)] (esempio stupido perchè basterebbe fare list(range(num)) ma spero di rendere l'idea)
ecc..

Infatti le list comprehension, creano un nuovo oggetto per ogni ciclo del for, quindi non avresti il problema che hai con l'operatore di ripetizione.
Ad esempio se fai:

a = [[4,2,8,6],[1,5,61,3]]
b = [[0 for _ in range(len(a[0]))] for _ in range(len(a))]
b[0][0] = a[0][0]

Noterai che b sarà uguale a [[4,0,0,0],[0,0,0,0]]

In questo caso, siccome andavi a ripetere un intero (che è un oggetto immutabile), avresti potuto usare l'operatore di ripetizione al posto della list comprehension interna e fare:
b = [[0] * len(a[0]) for _ in range(len(a))]
Plush (340 points)
2 4 7
commented Nov 14, 2019 by Plush (340 points)
Ok ho capito, grazie!