Переопределяющий задатчик для многомерного массива

#python #arrays #getter-setter

Вопрос:

Я пытаюсь переопределить сеттер и украсить его, но не понимаю, как это работает для многомерного массива.

 class Board:
    def __init__(self):
        self.board = []
        for k in range(3):
            self.board.append([0] * 3)

    def __str__(self):
        return 'n'.join([' '.join([str(num) for num in row]) for row in self.board])

    def __repr__(self):
        return self.__str__()

    def __setitem__(self, item, value):
        print(f'setting {item=}tt{value=}')
        self.board[item] = value

    def __getitem__(self, item):
        print(f'getting {item=}')
        return self.board[item]
 

Я обнаружил, что сеттер/геттер работает не так, как я ожидаю

 >>> board = Board()
>>> print(board)
0 0 0
0 0 0
0 0 0
>>> print(board[1][1])
getting item=1
0
>>> board[1][1] = 3
getting item=1
 

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

 >>> print(board)
0 0 0
0 3 0
0 0 0
>>> board[0] = [1, 3, 1]
setting item=0      value=[1, 3, 1]
>>> print(board)
1 3 1
0 3 0
0 0 0
 

Сеттер работает только в том случае, если я использую не более 1 индекса.

Комментарии:

1. board[1] возвращает объект списка . Что вы и установили.

2. Другими словами, причина board[1] , по которой возвращается list объект, заключается в том, что self.board это список списков .

3. Верно. Когда вы звоните board[1][1] , board[1] часть вызывает вашего получателя. Это возвращает обычный список Python, поэтому ваш второй [1] код вообще не связан с вашим кодом. Если вы хотите использовать синтаксис board[1,1] , например numpy, то ваш сеттер получит кортеж, и вы будете полностью контролировать ситуацию.

4. Итак, я должен использовать синтаксис «board[1,1]», если я не хочу добавлять другой класс для отдельных строк?

5. Очень важно отметить, что это вообще не массив, а значит, не многомерный массив. Это список

Ответ №1:

Я думаю,что было бы проще делать что-то, используя словарь значений вместо списка списков значений. В этом случае то, что вы назвали item аргументом методов __setitem__() и __getitem__() , было переименовано indices в приведенном ниже примере кода, так как теперь tuple оно имеет целочисленные значения: (row, col) .

 class Board:
    H, W = 3, 3  # Size in rows (height) X columns (width).

    def __init__(self):
        self.board = {(row, col): 0 for col in range(self.H)
                                        for row in range(self.W)}

    def __str__(self):
        return 'n'.join(', '.join(str(self[row, col])
                                for col in range(self.H))
                                    for row in range(self.W))

    __repr__ = __str__

    def __setitem__(self, indices, value):
        self.board[indices] = value

    def __getitem__(self, indices):
        return self.board[indices]


if __name__ == '__main__':
    board = Board()
    print(board)
    print(f'{board[1, 2]=}')
    print()

    board[1, 2] = 3
    print(f'{board[1, 2]=}')
    print(board)
 

Выход:

 0, 0, 0
0, 0, 0
0, 0, 0
board[1, 2]=0

board[1, 2]=3
0, 0, 0
0, 0, 3
0, 0, 0