This is because you are creating only one inner list object, and altering it.
In pseudocode, what you are doing is:
- Create a list called
line assigning [None, None, None] to it
- Create an empty list called
lines
- For three times:
-- Pick n items from the square list
-- Assign these three items to line[0], line[1] and line[2]
-- Append line to lines
So, what you are doing is assigning to individual items of line. This is important - you're not making a new object each time, you're changing individual items in the line list.
At the end of it all, line will point to the list [7, 8, 9]. And you can see lines as being substantially [line, line, line] (a list of three times the same object), so specifically now it will point to [[7,8,9], [7,8,9], [7,8,9]].
To solve this, possibly the solution that most keeps your original code is to re-define line after appending it. This way, the variable name line will refer to a different list each time, and you won't have this problem.
def getLines(square, N):
i = 0
line = [None]*N
lines = list()
for elt in square:
line[i] = elt
i += 1
if i == N:
lines.append(line)
line = [None]*N # Now `line` points to a different object
i = 0
return lines
Of course, there is leaner, more Pythonic code that can do the same thing (I see that an answer has already been given).
EDIT - Ok, here goes a somehow more detailed explanation.
Perhaps one of the key concepts is that lists are not containers of other objects; they merely hold references to other objects.
Another key concept is that when you change an item in a list (item assignment), you're not making the whole list object become another object. You're merely changing a reference inside it. This is something we give for granted in a lot of situations, but somehow becomes counter-intuitive when we'd want things to go the other way and "recycle" a list.
As I was writing in the comments, if list was a cat named Fluffy, every time you're appending you're creating a mirror that points to Fluffy. So you can dress Fluffy with a party hat, put a mirror pointing to it, then give Fluffy a clown nose, put on another mirror, then dress Fluffy as a ballerina, add a third mirror, and when you look at the mirrors, all three of them will show the ballerina Fluffy. (Sorry Fluffy).
What I mean is that in practice in your first script, when you do the append:
lines.append(line)
by the first concept I mentioned, you are not making lines contain the current status of line as a separate object. You are appending a reference to the line list.
And when you do,
line[i] = elt
by the second concept, of course line is always the same object; you're just changing what's referenced at the i-th position.
This is why, at the end of your script, lines will appear to "contain three identical objects": because you actually appended three references to the same object. And when you ask to see the content of lists, you will read, three times, the list object in its current status.
In the code I provided above, I re-define the name lists to make it reference a brand new list every time it's been appended to lists:
lines.append(line)
line = [None]*N # Now `line` points to a different object
This way, at the end of the script I have "three different cats" appended, and each one was conveniently named Fluffy just until I had appended it, to give room for a new Fluffy list after that.
Now, in your second script, you do something similar. The key instruction is:
lines = [[None]*N]*N # Need to be initialized to be able to index it.
In this line, you are creating two objects:
- the list [None, None, None]
- the list named lines, which contains N references to the same list [None, None, None].
What you did was just to create straight away Fluffy and the three mirrors pointing at him.
In fact if you change lines[0][2], or lines[1][2], you're just changing the same item [2] of your same Fluffy.
What you actually wanted to do is,
lines = [[None]*N for i in range(N)]
which creates three different cats - I mean, lists, and have lines point to the three.