Sometimes, we need to initialize a list with a certain number of nested lists, for example, a list with 3 lists of length 3 that can represent a Tic-tac-toe board.

>>> board = [[''] * 3] * 3
>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>> board[1][2] = 'X'
>> board
[['', '', 'X'], ['', '', 'X'], ['', '', 'X']]

The output is not expected, why?

Because the outer list is made of three references to the same inner list, a fact that you can see from id(board[0]) == id(board[1]) is True. While it is unchanged, all seems right. After placing a mark in row 1, column 2, it reveals that all rows are alias referring to the same object.

In essence, the above code behaves like:

row = [''] * 3
board = []
for _ in range(3):
    board.append(row)

It is much obvious that the same row is appended 3 times to board.

The correct way to achieve our goal is:

>>> board = [[''] * 3 for _ in range(3)]
>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>> board[1][2] = 'X'
>> board
[['', '', ''], ['', '', 'X'], ['', '', '']]

Actually, it is equivalent to this code:

board = []
for _ in range(3):
    row = [''] * 3
    board.append(row)

Reference

Fluent Python