I'm trying to create a simple Game of Life by storing Cell objects into a set (have to use classes) and I reach the problem in which I cannot add the cell object into the Set because it is unhashable... is there any way around this? Thanks!
class Cell():
def __init__(self, row, col):
self.row = row
self.col = col
def getRow(self):
return self.row
def getCol(self):
return self.col
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
class SparseLifeGrid(set):
# Creates a new infinite-sized game grid with all cells set to dead.
def __init__(self):
self.rowList = []
self.colList = []
self.cellSet = set()
def __add__(self, cell):
self.cellSet.add(cell)
self.rowList.append(cell.getRow())
self.colList.append(cell.getCol())
def minRange(self):
#Returns a 2-tuple (minrow, mincol) that contains the minimum
#row index and the minimum
#column index that is currently occupied by a live cell.
#None is returned if there are no alive cells.
return (sorted(self.rowList)[0], sorted(self.rowList)[0])
def maxRange(self):
#Returns a 2-tuple (maxrow, maxcol) that contains the
#maximum row index and the maximum
#column index that is currently occupied by a live cell.
#None is returned if there are no live cells.
return (sorted(self.rowList,reverse = True)[0],\
sorted(self.colList,reverse = True)[0])
def clearCell(self, row, col):
#Clears the individual cell (row, col) and sets it to dead.
#If the cell is already dead, no action is taken.
for item in self:
if item == Cell(row,col):
self.remove(item)
def setCell(self, row, col):
#Sets the indicated cell (row, col) to be alive.
#If the cell is already alive, no action is taken.
self.__add__(Cell(row,col))
def isLiveCell(self, row, col):
#Returns a boolean value indicating if the given
#cell (row, col) contains a live organism.
return Cell(row,col) in self
def numLiveNeighbors(self, row, col):
#checks how many live adjacents (not diagonals I think) a cell has
surround = 0
if self.isLiveCell(row+1,col):
surround += 1
if self.isLiveCell(row-1,col):
surround += 1
if self.isLiveCell(row, col+1):
surround += 1
if self.isLiveCell(row, col-1):
surround += 1
return surround
G = SparseLifeGrid()
G.setCell(2,3)
You need to make your Cell instances immutable, then create a __hash__
method that always remains the same. If you can't use tuples directly, here's an alternative (that borrows only a little from tuple
):
class Cell:
# each cell will have exactly two values, so no need for __dict__
__slots__ = ["row", "col"]
# set up values at construction time using super().__setitem__()
def __init__(self, row, col):
super().__setitem__("row", row)
super().__setitem__("col", col)
return self
# a Cell is intended to be immutable, so __setattr__ always raises an error
def __setattr__(self, name, value):
if hasattr(self, name):
raise AttributeError("{!r} object attribute {!r} is read-only"
.format(self.__class__.__name__, name))
else:
raise AttributeError("{!r} object has no attribute {!r}"
.format(self.__class__.__name__, name))
# comparison operators
def __eq__(self, other):
return (isinstance(other, Cell) and
self.row == other.row and
self.col == other.col)
def __ne__(self, other):
return not self == other
# hash function, with a value borrowed from a tuple
def __hash__(self):
return hash((self.row, self.col))
This is rather a lot of work though, for something that is equivalent to:
Cell = collections.namedtuple(["row", "col"])
Your grid class also has some issues. For instance, you're overriding __add__
, which is used for implementing the addition operator, but you don't return anything, so it won't work like it would be expected to. I suspect you mean to be overriding the add
method (with no underscores) instead. However, if that's the case, you'll want to be sure you call super().add()
with appropriate arguments if you want your grid to actually function as a set.
Also, min(lst)
should be much faster than sorted(lst)[0]
(O(N) rather than O(N log N)).