Разгадка управления памятью списков Python

Уровень 0 — Введение:

Списки — неотъемлемая часть экосистемы Python. Для новичков список — это набор типов данных или объектов, заключенных в [ ] и разделенных запятой. Они гетерогенны, изменчивы, упорядочены и индексируемы, что делает их одной из самых OP (мощных) структур данных. В этой статье демонстрируется управление памятью списков с помощью классической игры «Крестики-нолики».

Уровень 1 — Построение крестиков-ноликов:

В этом разделе мы воспроизведем игру в крестики-нолики, используя списки Python. Мы могли бы использовать приведенный ниже синтаксис, чтобы получить общее представление о создании списка.

# empty list
ls = []
# list creation with elements
ls = [1, 2, 3, "Python", 3.8]
print(ls)
output: 
[1, 2, 3, 'Python', 3.8]

Опираясь на приведенный выше синтаксис, мы можем использовать символ «*» для умножения элементов списка и, таким образом, создания доски.

# board creation
board = [[“ ”] * 3 ] * 3
print(board)
output:
[[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]

Расшифровка внутренних блоков создания доски:
1. [“ ”] → пустой список
2. [“ ”] * 3 → выдаст [“ ”, “ ”, “ ”]
3. [[“ ”] * 3 ] * 3 → даст [[“ ”, “ ”, “ ”], [“ ”, “ ”, “ ”], [“ ”, “ ”, “ ”]]< br /> 4. Следовательно, присваиваем [[“ ”] * 3 ] * 3 переменной board.

Приведенный выше вывод [[“ ”, “ ”, “ ”], [“ ”, “ ”, “ ”], [“ ”, “ ”, “ ”]] можно представить как 3 X 3 крестики-нолики.

Уровень 2 — Выигрыши:

Чтобы получить доступ к элементам, мы могли бы использовать подход board[n-1][m-1], где n = строка и m = столбец. Например,
1. Чтобы получить первый элемент, мы могли бы передать board[0][0] (индекс начинается с 0),
2. Второй элемент в первой строке, board[0][1]
3. Второй элемент в первом столбце, board[1][0]
4. Последний элемент → board[2][ 2]
5. Если мы попытаемся получить элементы вне списка, доски[2][3] или доски[3][2], мы получим IndexError: list индекс вне допустимого диапазона.

Теперь, когда мы привыкли к созданию списка, дизайну доски для игры в крестики-нолики и доступу к элементам с доски, давайте перейдем к МОМЕНТУ, КОТОРОГО МЫ ДОЛЖНЫ ЖДАТЬ!

Первым оптимальным ходом в игре будет размещение «X» в любом углу доски.

# Assigning the first move(element) to "X"
board[0][0] = "X"

Ожидания:

Реальность:

print(board)

Поздравляем с победой в крестики-нолики одним ходом. Был ли это счастливый случай? Давайте узнаем в следующем разделе.

Уровень 3 — расшифровка

Прежде чем мы углубимся в то, что именно произошло, давайте рассмотрим операции в мельчайших деталях. Когда мы просматриваем адреса памяти всех блоков пустой переменной board(используя id()), все элементы указывают на один адрес(представленный в приведенном ниже коде и на изображении ).

# board creation
board = [[“ ”] * 3 ] * 3
# memory addresses of board rows
id(board[0]) == id(board[1]) == id(board[2])
output: 
True
# memory addresses of random elements 
id(board[0][0]) == id(board[1][2]) == id(board[2][1])
output: 
True

Когда мы обновляем board[0][0] = «X», для board[0][0] создается новый id для хранения значения «X». Поскольку все три строки доски (board[0], board[1], board[2]) указывают на одну и ту же ячейку памяти, список [“X”, “ ”, “ ” ] обновляется до соответствующих экземпляров строки. Мы можем проверить это, проверив id(board[0][0]) == id(board[1][0]) == id(board[2][0]), который возвращает Истина.

Поэтому, мой друг, ты можешь выиграть крестики-нолики одним ходом. Ну, можно ли не выиграть партию с первого хода?

Финал:

В последнем разделе позвольте мне продемонстрировать способ избежать таких сценариев при работе со списками.

# board creation using list comprehension
board = [[" "]*3 for i in range(3)]
print(board)
output:
[[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
# first move
board[0][0] = "X"
print(board)
output:
[['X', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
# comparing ids
id(board[0][0]) == id(board[1][0]) == id(board[2][0])
output:
False

Я надеюсь, что эта статья помогла вам понять внутренности управления памятью в списках Python. Я придумаю больше такого содержания в моих следующих статьях.

Любопытный? Взгляните на управление памятью кортежа.

Использованная литература:

  1. https://github.com/satwikkansal/wtfpython#-a-tic-tac-toe-where-x-wins-in-the-first-attempt
  2. https://puzzling.stackexchange.com/questions/30/what-is-the-optimal-first-move-in-tic-tac-toe