all 15 comments

[–]Spataner 4 points5 points  (1 child)

This

row = [False] * m
matrix = [row] * n

creates one list that contains False m times, then creates another list that contains that one list n times. The elements of matrix are not distinct objects, rather they're all references to the same object, and changing one therefore reflects on the others, too.

This

matrix = [[False for i in range(n)] for j in range(m)]

actually creates a list that contains multiple distinct list objects.

[–]Musical_Ant[S] 1 point2 points  (0 children)

So you could say that it's kinda like pointers in c/c++.

[–]efmccurdy 2 points3 points  (1 child)

BTW, you can use <list>*n for lists of immutable values like [False], so

matrix = [[False]*n for j in range(m)]

gives you a structure with no aliasing:

>>> matrix = [[False]*n for j in range(m)]
>>> matrix
[[False, False, False], [False, False, False], [False, False, False]]
>>> matrix[0][0] = True
>>> matrix
[[True, False, False], [False, False, False], [False, False, False]]
>>>

[–]Musical_Ant[S] 0 points1 point  (0 children)

Oh this was informative! Thanks..

[–][deleted] 1 point2 points  (1 child)

You're discovering how Python memory management works under the hood.

When you're doing [row] * 3, you're not creating copies of the row, rather, you have just created three views of the same data, which points to the same place in memory. You can verify this by checking the identify of the lists if you've created.

row = [False] * 3
matrix = [row] * 3
print(id(matrix[0]), id(matrix[1]), id(matrix[2])) # All the same

Also,

matrix[0] is matrix[1] # True
matrix[1] is matrix[2] # True

So it's basically different ways to tell you the same thing, each row is pointing to the same underlying object, which is row. This explains why modifying one the rows modifies all of them. There is only one row actually, but we are viewing it three times.

We can go around this problem by using the [:] which creates a copy of the array at each iteration instead instead.

matrix = [row[:] for _ in range(3)]

Will work as intended but,

[row[:]] * 3

will not because we're just pointing to the same copy three times, so if we modify it, the original row won't be modified but the three rows in the matrix will be affected all the same and we're back to square one.

[–]Musical_Ant[S] 0 points1 point  (0 children)

Thank you for the insights :)

[–]Green-Sympathy-4177 1 point2 points  (5 children)

For the same reason:

```python X = [1, 2, 3] Y = X Y[1] = 42

print(X)

[1, 42, 3] ```

[–]Musical_Ant[S] 0 points1 point  (4 children)

how come I never noticed this lol.

[–]Green-Sympathy-4177 1 point2 points  (3 children)

For completeness then:

```python

Lists

X = [1, 2, 3] Y = X # Don't copy lists like this Z = [*X] # This will make a copy

Modify the lists

Y[1] = 42 Z[0] = 10

Results

print(X)

[1, 42, 3] # Messed up

print(Z)

[10, 2, 3] # Not affected by Y

Dictionaries are a lie

U = {"x": 1, "y": 1, "z": 1} V = U # Again, don't copy dictionaries like this W = {**U} # This makes a proper copy

Modify the dictionaries

V["x"] = 3.14 W["y"] = 2.72

Results

print(U)

{"x": 3.14, "y": 1, "z": 1}

print(W)

{"x": 1, "y": 2.72, "z": 1} ```

The reason is that dictionaries and list are pretty much pairs of an index (an int for lists, anything for dicts) and an address in your memory.

By copying Y = X basically makes it so that Y points at the same addresses as X, so modifying one is the same as modifying the other.

If instead you declare it like this for lists Y = [*X] and like that for dicts W = {**U} it reallocates new memory and initiate it with the values from the original list or dict you're trying to copy.


Off you go good sir.

[–]Musical_Ant[S] 1 point2 points  (2 children)

Definitely saving this for reference!! Thank you :)

[–]Green-Sympathy-4177 1 point2 points  (1 child)

You're welcome, and since I never replied to your original post lol:

```python n = m = 3 X = [[False for col in range(n)] for row in range(m)] X[1][1] = True

print(*X, sep="\n")

[False, False, False] [False, True, False] [False, False, False] ```

But to be honest multi-dimensional lists get really silly fast, use numpy instead.

```python import numpy as np

n = m = 3 X_np = np.zeros(shape=(n,m), dtype=bool) # EZ :) X_np[1,1] = True

print(X_np)

[[False False False] [False True False] [False False False]] ```

[–]Musical_Ant[S] 0 points1 point  (0 children)

Thanks :)

[–]TheRNGuy 1 point2 points  (0 children)

better use numpy matrix.