You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于MutableSequence实现无numpy依赖的9×9数独二维数组类

如何基于collections.abc.MutableSequence实现数独数组类?

背景

几天前我发现了一个通过继承collections.abc.MutableSequence实现自定义列表类的示例,代码如下:

class TypedList(MutableSequence):

    def __init__(self, oktypes, *args):
        self.oktypes = oktypes
        self.list = list()
        self.extend(list(args))

    def check(self, v):
        if not isinstance(v, self.oktypes):
            raise TypeError(v)

    def __len__(self): return len(self.list)

    def __getitem__(self, i): return self.list[i]

    def __delitem__(self, i): del self.list[i]

    def __setitem__(self, i, v):
        self.check(v)
        self.list[i] = v

    def insert(self, i, v):
        self.check(v)
        self.list.insert(i, v)

    def __str__(self):
        return str(self.list)

使用示例:

tl = TypedList(int)
# 正常执行
tl.append(1)
# 会触发TypeError
tl.append('1')

需求与问题

我想以类似方式实现一个继承自MutableSequence的二维SudokuArray类,用于管理数独游戏的数组。以下是我写的一个不可行的版本:

class SudokuArray(MutableSequence):

    def __init__(self, n_rows=9, n_columns=9, init_value=None):
        self.n_rows = n_rows
        self.n_columns = n_columns
        self.array = [[init_value for _ in range(0, self.n_columns, 1)] for _ in range(0, self.n_rows, 1)]

    def check(self, row_number, column_number, number):
        if number in self.get_row_values(row_number) or\
                                         number in self.get_column_values(column_number) or\
                                         number in self.get_nonet_values(row_number, column_number):
            raise ExistentNumberError("Existent number in row, column or nonet",
                                      number, row_number, column_number)

    def __len__(self): return self.n_rows, self.n_columns

    def __getitem__(self, row_number, column_number): return self.array[column_number][row_number]

    def __delitem__(self, row_number, column_number): del self.array[column_number][row_number]

    def __setitem__(self, row_number, column_number, number):
        self.check(number, row_number, column_number)
        self.array[column_number][row_number] = number

    def insert(self, row_number, column_number, number):
        self.check(row_number, column_number, number)
        self.array.insert(row_number, column_number, number)

    def __str__(self):
        return str(self.array)

    def get_row_values(self, row_number):
        # todo implement get_row_values method
        pass

    def get_column_values(self, column_number):
        # todo implement get_column_values method
        pass

    def get_nonet_values(self, row_number, column_number):
        # todo implement get_nonet_values method
        pass

class ExistentNumberError(Exception):
    def __init__(self, message, number, row_number, column_number):
        super().__init__(message)
        self.number = number
        self.row_number = row_number
        self.column_number = column_number

使用示例:

sudoku_array = SudokuArray()
sudoku_array[0][0] = 1
# 会触发ExistentNumberError
sudoku_array[0][0] = 1

我不想用numpy.array,因为9×9的数组用numpy过于复杂,而且希望避免外部依赖。请问有什么可行的实现思路?


可行实现思路与代码

核心问题修正

MutableSequence是为一维序列设计的抽象基类,它的方法(如__getitem__、__setitem__)只接受单一索引参数,直接实现二维访问会违反接口约定。正确的做法是让SudokuArray作为行的序列,每个行是一个自定义序列类,负责列的访问与校验。

另外,数独数组是固定大小的,不需要insert、append这类动态增删元素的方法,可直接抛出NotImplementedError禁用。

完整实现代码

from collections.abc import MutableSequence

class ExistentNumberError(Exception):
    def __init__(self, message, number, row_number, column_number):
        super().__init__(message)
        self.number = number
        self.row_number = row_number
        self.column_number = column_number

class SudokuRow(MutableSequence):
    def __init__(self, parent, row_idx):
        self.parent = parent
        self.row_idx = row_idx
        self._data = [None] * parent.n_columns

    def check(self, col_idx, value):
        if value is None:
            return
        # 检查行、列、3x3宫
        if value in self._data or \
           value in self.parent.get_column_values(col_idx) or \
           value in self.parent.get_nonet_values(self.row_idx, col_idx):
            raise ExistentNumberError(
                f"数字{value}已存在于行{self.row_idx}、列{col_idx}或对应3x3宫",
                value, self.row_idx, col_idx
            )

    def __len__(self):
        return len(self._data)

    def __getitem__(self, col_idx):
        return self._data[col_idx]

    def __setitem__(self, col_idx, value):
        self.check(col_idx, value)
        self._data[col_idx] = value

    def __delitem__(self, col_idx):
        # 数独不允许删除元素,直接抛出错误
        raise NotImplementedError("数独数组不支持删除操作")

    def insert(self, col_idx, value):
        # 数独是固定大小,不允许插入
        raise NotImplementedError("数独数组不支持插入操作")

    def __str__(self):
        return str(self._data)

class SudokuArray(MutableSequence):
    def __init__(self, n_rows=9, n_columns=9):
        if n_rows != 9 or n_columns !=9:
            raise ValueError("标准数独数组必须是9x9")
        self.n_rows = n_rows
        self.n_columns = n_columns
        self._rows = [SudokuRow(self, i) for i in range(n_rows)]

    def get_row_values(self, row_idx):
        return [v for v in self._rows[row_idx] if v is not None]

    def get_column_values(self, col_idx):
        return [self._rows[r][col_idx] for r in range(self.n_rows) if self._rows[r][col_idx] is not None]

    def get_nonet_values(self, row_idx, col_idx):
        # 计算3x3宫的起始行和列
        start_row = (row_idx // 3) * 3
        start_col = (col_idx // 3) * 3
        values = []
        for r in range(start_row, start_row+3):
            for c in range(start_col, start_col+3):
                val = self._rows[r][c]
                if val is not None:
                    values.append(val)
        return values

    def __len__(self):
        return self.n_rows

    def __getitem__(self, row_idx):
        return self._rows[row_idx]

    def __setitem__(self, row_idx, value):
        # 不允许直接替换整行,如需修改请逐列操作
        raise NotImplementedError("不允许直接替换整行,请逐列修改")

    def __delitem__(self, row_idx):
        raise NotImplementedError("数独数组不支持删除行")

    def insert(self, row_idx, value):
        raise NotImplementedError("数独数组不支持插入行")

    def __str__(self):
        return "\n".join(str(row) for row in self._rows)

使用示例

sudoku = SudokuArray()
# 正常赋值
sudoku[0][0] = 1
# 尝试在同一行重复赋值,触发错误
try:
    sudoku[0][1] = 1
except ExistentNumberError as e:
    print(e)  # 输出:数字1已存在于行0、列1或对应3x3宫

# 尝试在同一列赋值
sudoku[1][0] = 2
try:
    sudoku[2][0] = 2
except ExistentNumberError as e:
    print(e)  # 输出:数字2已存在于行2、列0或对应3x3宫

# 尝试在同一3x3宫赋值
sudoku[0][1] = 3
try:
    sudoku[1][2] = 3
except ExistentNumberError as e:
    print(e)  # 输出:数字3已存在于行1、列2或对应3x3宫

说明

  • 实现严格遵循MutableSequence接口约定,通过嵌套的SudokuRow类实现二维访问。
  • 所有违反数独规则的赋值操作都会触发ExistentNumberError。
  • 禁用了数独不需要的动态增删操作,保证数组大小固定为9x9。
  • 完全基于Python标准库,无外部依赖。

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 14:31:02