0

I think I'm misusing the concept of subclass. I'm working on a hobby project with Grids and Cells.

What I have, is the implementation of a Cell class, and its subclass HexCell which basically redefines many of the attributes/methods like so:

class Cell: 
 def __init__(self, row_loc, col_loc):
 self.row = row_loc
 self.col = col_loc
 self.links = set()
 self.neighbors = 4*[None]
 def __repr__(self):
 return f'Cell @({self.row},{self.col})'
 def link(self, other, bidir = True):
 self.links.add(other)
 if bidir: other.links.add(self)

Then I have a subclass that is the HexGrid which follows a similar structure with new parameters.

class HexCell(Cell):
 def __init__(self, r_out, th_around):
 # I'm indexing Hex cells around a center cell 
 # instead of by rows and columns; Prefixed hex
 # as they follow the hexagon, and not regular polar coordinates. 
 self.hex_r = r_out
 self.hex_th = th_around
 self.neighbors = 6*[None]
 self.links = set()
 def __repr__(self):
 return f"HexCell @[{self.hex_r}, {self.hex_th}]"
 def bind(self, other, to_dir):
 to_dir = to_dir % 6
 if (self.neighbors[to_dir] is None):
 self.neighbors[to_dir] = other
 other.neighbors[to_dir - 3] = self
 # Hexagonal grids share neighbors. 
 other_1 = other.neighbors[to_dir - 2]
 if (self.neighbors[to_dir - 1] is None) & (other_1 is not None):
 self.bind(other_1, to_dir - 1)
 other_5 = other.neighbors[to_dir - 4]
 if (self.neighbors[to_dir - 5] is None) & (other_5 is not None):
 self.bind(other_5, to_dir - 5)

In this case, the method self.link(other) is shared, but other attributes change from rectangular grid to hexagonal like the locaion from (row, col) to (hex_r, hex_th), or neighbors as a 4-list or 6-list. Thus I'd like these attributes to be dependent on a another cell-type attribute and transferred down to the subclass.

jonrsharpe
123k31 gold badges278 silver badges489 bronze badges
asked Jun 3, 2018 at 12:18
1
  • This breaks the Liskov substitution principle; HexCell claims to subclass Cell but it doesn't share the same public attributes and seems to have a different method for the same behaviour. Commented Jun 3, 2018 at 12:22

2 Answers 2

3

Correct use of subclassing needs to obey the following substitution principle:

If there are some objects x_1 of type T_1 and x_2 of type T_2 such that issubclass(T_2, T_1) == True, then any property that applies to x_1 must also apply for x_2.

In other words, you expect subclassing to implement new behaviours, not to change existing behaviours.

In you example, the change of coordinate system itself is a change of behaviour and thus HexCell should not inherit from Cell.

What you can do is create a base class BaseCell that encapsulates the common behaviour between Cell and HexCell and inherit from it.

class BaseCell:
 def __init__(self):
 self.links = set()
 self.neighbors = []
 def add_neighbor(self, other):
 self.neighbors.append(other)
 def link(self, other, bidirectional=True):
 self.links.add(other)
 if bidirectional:
 other.link(self, bidirectional=False)
class Cell(BaseCell):
 def __init__(self, row_loc, col_loc):
 self.row = row_loc
 self.col = col_loc
 super().__init__()
 def __repr__(self):
 return f'Cell @({self.row},{self.col})'
class HexCell(Cell):
 def __init__(self, r_out, th_around):
 self.hex_r = r_out
 self.hex_th = th_around
 super().__init__()
 def __repr__(self):
 return f"HexCell @[{self.hex_r}, {self.hex_th}]"
 def bind(self, other, to_dir):
 ...
answered Jun 3, 2018 at 12:39
Sign up to request clarification or add additional context in comments.

Comments

1

Your Cell class is in fact not an abstract "Cell", but a square cell in two-dimensional space (has exactly 4 neighbours, has "row" and "col" position). Such cell may not be subclassed by a hex cell, because hex cell is just a different type of cell : )

As you noticed, the only common things are link() method and links attribute. If you insist on subclassing, you could create something like:

class LinkedObject():
 def __init__(self):
 self.links = set()
 def link(self, other, bidir = True):
 self.links.add(other)
 if bidir: other.links.add(self)
class SquareCell(LinkedObject):
 # "Cell" class here
class HexCell(LinkedObject):
 # HexCell here
answered Jun 3, 2018 at 12:49

1 Comment

Yes, this shapes it more like I wanted it from the beginning. Thanks.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.