几天前,我发现了下一个如何通过从 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)
TypedList的使用示例:
tl = TypedList(int)
# ok
tl.append(1)
# next TypeError will be raised
tl.append('1')
我的问题:
我想知道是否有方法以类似的方式实现二维数独数组(MutableSequence)类来在游戏中管理数独数组?
为了给您一个想法,下面您可以看到 SudokuArray(MutableSequence) 的可能实现(非操作):
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
# next ExistentNumberError will be raised
sudoku_array[0][0] = 1
当然我可以使用 numpy.array 但我认为我们的 9x9 数组使用 numpy 非常简单。我也想避免依赖。
有什么想法吗?
您的尝试中存在以下问题:
if
中的check
语句缺少左括号。__len__
应该返回一个数字,而不是一个元组。__getitem__
和类似方法不应采用两个索引参数,而应采用一个。然而,这个参数可以是一个元组(2 个坐标)insert
和 __delitem__
方法不应在数独上使用:您不想更改任何行上的条目数。在下面的更正中,我仍然会包含它们,因为也许你有充分的理由?get_***_values
方法未实现。这是建议的代码:
from collections.abc import MutableSequence
from math import isqrt
class SudokuArray(MutableSequence):
def __init__(self, size=9, init_value=None):
self.size = size
self.array = [[init_value for _ in range(self.size)] for _ in range(self.size)]
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.size * self.size
def __getitem__(self, coordinates):
return self.array[coordinates[0]][coordinates[1]]
def __delitem__(self, coordinates):
del self.array[coordinates[0]][coordinates[1]]
def __setitem__(self, coordinates, number):
self.check(coordinates[0], coordinates[1], number)
self.array[coordinates[0]][coordinates[1]] = number
def insert(self, coordinates, number):
self.check(coordinates[0], coordinates[1], number)
self.array[coordinates[0]].insert(coordinates[1], number)
def __str__(self):
return "\n".join(" ".join(map(str, row)) for row in self.array).replace("None", ".")
def get_row_values(self, row_number):
return self.array[row_number][:]
def get_column_values(self, column_number):
return [row[column_number] for row in self.array]
def get_nonet_values(self, row_number, column_number):
width = isqrt(self.size)
row_number -= row_number % width
column_number -= column_number % width
return [val for row in self.array[row_number:row_number+width]
for val in row[column_number:column_number+width]]
使用示例:
sudoku = SudokuArray()
sudoku[1, 2] = 9
sudoku[2, 3] = 8
sudoku[5, 5] = 4
sudoku[4, 4] = 3
sudoku[3, 3] = 6
print(sudoku)