Few days ago I found the next example of howto implement customs list classes via subclassing the MutableSequence from collections.abc.
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)
Example of use of TypedList:
tl = TypedList(int)
# ok
tl.append(1)
# next TypeError will be raised
tl.append('1')
My Question:
I was wondering if there is way of implementing an two-dimensions SudokuArray(MutableSequence) class in similar manner for mananing a sudoku array in a game?
For giving you an idea, below you can see a possible implementation (non-operative) of that 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
Example of use of SudokuArray:
sudoku_array = SudokuArray()
sudoku_array[0][0] = 1
# next ExistentNumberError will be raised
sudoku_array[0][0] = 1
Of course I could use numpy.array but I think our 9x9 array is very simple for using numpy. Also I want to avoid dependencies.
Any idea?
There are these issues in your attempt:
if
statement in check
lacks an opening parenthesis.__len__
should return a number, not a tuple.__getitem__
and similar methods should not take two index arguments, but one. However, this argument can be a tuple (of 2 coordinates)__setitem__
the call to self.check
has arguments in the wrong order.insert
and __delitem__
methods should not be used on a Sudoku: you don't want to change the number of entries on any row. In the below correction I will still include them, as maybe you have a good reason for this?get_***_values
methods were not implemented.Here is the suggested code:
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]]
Example use:
sudoku = SudokuArray()
sudoku[1, 2] = 9
sudoku[2, 3] = 8
sudoku[5, 5] = 4
sudoku[4, 4] = 3
sudoku[3, 3] = 6
print(sudoku)