Python重写多维数组setter时嵌套索引赋值不触发的问题
根本原因
Python的嵌套索引赋值是从左向右逐层解析执行的,board[1][1] = 3 等价于以下两步:
- 执行
temp = board.__getitem__(1),拿到第一层索引对应的返回值,也就是你代码里的原生Python列表 - 执行
temp.__setitem__(1, 3),对第一步返回的原生列表执行赋值操作
你只重写了外层Board类的__setitem__方法,内层返回的原生列表的__setitem__还是Python默认实现,自然不会触发你写的自定义逻辑。而单层赋值board[0] = [1,3,1]是直接调用Board类的__setitem__,所以符合预期。
可行解决方案
下面给出两种常用的实现方案:
方案1:支持多维元组索引
Python的魔法方法__getitem__、__setitem__天然支持接收元组作为索引参数,你可以修改方法逻辑,直接用board[1,1] = 3的形式完成多维赋值,实现成本最低。
修改后的代码示例:
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): # 索引为元组时按多维处理 if isinstance(item, tuple): x, y = item print(f'setting ({x},{y}) = {value}') self.board[x][y] = value return print(f'setting {item=}\t\t{value=}') self.board[item] = value def __getitem__(self, item): if isinstance(item, tuple): x, y = item print(f'getting ({x},{y})') return self.board[x][y] print(f'getting {item=}') return self.board[item]
测试效果:
>>> board = Board() >>> board[1,1] = 3 setting (1,1) = 3 >>> print(board[1,1]) getting (1,1) 3
- 优点:实现简单,没有额外封装开销
- 缺点:需要调整调用方的索引写法,不兼容原有嵌套
[][]的调用形式
方案2:封装内层列表为自定义类
让__getitem__返回的不是原生列表,而是同样重写了__setitem__的自定义行类,兼容原有嵌套[][]的调用形式。
代码示例:
class BoardRow: def __init__(self, row_data): self.row = row_data def __getitem__(self, item): print(f'getting row item={item}') return self.row[item] def __setitem__(self, item, value): print(f'setting row item={item}\t\tvalue={value}') self.row[item] = value class Board: def __init__(self): self.board = [] for k in range(3): # 内层用自定义BoardRow类替代原生列表 self.board.append(BoardRow([0] * 3)) def __str__(self): return '\n'.join([' '.join([str(num) for num in row.row]) for row in self.board]) def __repr__(self): return self.__str__() def __setitem__(self, item, value): print(f'setting {item=}\t\t{value=}') self.board[item] = value def __getitem__(self, item): print(f'getting {item=}') return self.board[item]
测试效果:
>>> board = Board() >>> board[1][1] = 3 getting item=1 setting row item=1 value=3 >>> print(board[1][1]) getting item=1 getting row item=1 3
- 优点:完全兼容原有嵌套索引的调用写法,自定义逻辑可以灵活插入到每一层索引操作中
- 缺点:需要额外实现自定义行类,维度更高时需要多层封装,实现成本略高
内容的提问来源于stack exchange,提问作者Dikzamen
相关产品推荐
相关产品推荐

