Appending values to a Python member variable

OLHAR :

I am new to Python and OO programming in general and so please forgive the, probably, very poorly designed code (any tips would be greatly appreciated).

In this, contrived, MWE which is purely to illustrate a similar problem in my larger project. I want to iterate through a 3x3 grid and fill it, so that it contains all the digits 1-9, the only values I can change are ones which are currently set a 0. i.e. If the grid currently has the digits 1-7 and two positions are 0 then one of these 0s becomes an 8 and one becomes a 9, in this instance there are two solutions since the order of the 8 and 9 can also be swapped.

I have designed a backtracking solver (runSolver()) and it does solve this problem, what I am struggling to do though is store the solutions when I reach them. I have added a print statement for when a solution is reached and this prints out the solution as expected, I then try to append this solution to a list and instead of appending the solution that has just been found it instead appends the initial, unsolved, grid.

class Grid:

    def __init__(self):
        self.grid = np.zeros((3, 3))

    def writeGrid(self, grid):
        self.grid = grid

    def printGrid(self):
        print(self.grid)

    def getValue(self, col, row):
        return self.grid[row][col]

    def setValue(self, col, row, num):
        self.grid[row][col] = num


class Solver:

    def __init__(self, grid):
        self.grid = grid
        self.solutions = []
        self.n_solutions = 0

    def isValid(self, num):
        for i in range(3):
            for j in range(3):
                if self.grid.getValue(i, j) == num:
                    return False
        return True

    def runSolver(self):
        for row in range(3):
            for col in range(3):
                if (self.grid.getValue(col, row)) == 0:
                    for num in range(1,10):
                        if self.isValid(num):
                            self.grid.setValue(col, row, num)
                            self.runSolver()
                            self.grid.setValue(col, row, 0)
                    return
        self.grid.printGrid()             # this line prints the actual solutions when reached (it works)
        self.solutions.append(self.grid)  # this should append the solution to 'solutions'
        self.n_solutions += 1             # keeps track of how many solutions there are

The main function which actually shows the problem is then,

# Set up game
gameGrid = Grid()
gameGrid.writeGrid([[1, 4, 5],
                    [0, 6, 0],
                    [7, 8, 9]])
solverGrid = Solver(gameGrid)

# Run the solver
solverGrid.runSolver()

# This should print out the found solutions, 
# It actually prints out the initial, unsolved, grid twice
for i in range(solverGrid.n_solutions):
    solverGrid.solutions[i].printGrid()

From some searching online I think that I may be getting confused between instance attributes and class attributes and the scope with which they are accessible however I am really not sure.

Delena Malan :

When you run self.solutions.append(self.grid) you basically just append a reference to the self.grid to self.solutions. So at the end of your runSolver you have a list of references in self.solutions that all point to the same object.

This has to do with the fact that both your Grid object and Numpy arrays are mutable objects. In contrast to Python strings, for example, when you modify them (with self.grid.setValue(col, row, num) for example), the same object is modified in place instead of a new object being created.

Here is the same issue illustrated with a list of lists:

>>> l = []
>>> x = [1]
>>> l.append(x)
>>> l
[[1]]
>>> x.append(2)
>>> l.append(x)
>>> l
[[1, 2], [1, 2]]

You'll have to create a copy of the grid every time you add it to self.solutions so that you can have a "snapshot" of the grid as it was at that point.

You could do something like this:

class Grid:

    def __init__(self, grid=None):
        if grid == None:
            self.grid = np.zeros((3, 3))
        else:
             # Copy the array, otherwise we'll have the same mutability issue as above.
            self.grid = np.copy(grid)

In runSolver:

        grid_copy = Grid(self.grid.grid)
        self.solutions.append(grid_copy) # this should append the solution to 'solutions'

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=351175&siteId=1