【问题标题】:Python classes and typesPython 类和类型
【发布时间】:2018-06-03 12:18:38
【问题描述】:

我认为我误用了子类的概念。我正在使用 Grids 和 Cells 开展一个爱好项目。

我所拥有的是 Cell 类及其子类 HexCell 的实现,它基本上重新定义了许多属性/方法,如下所示:

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)

然后我有一个子类HexGrid,它遵循类似的结构和新参数。

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)

在这种情况下,方法self.link(other) 是共享的,但其他属性从矩形网格变为六边形,例如从(row, col)(hex_r, hex_th) 的位置,或者neighbors 作为4 列表或6 列表。因此,我希望这些属性依赖于另一个单元类型属性并转移到子类。

【问题讨论】:

  • 这打破了里氏替换原则; HexCell 声称是 Cell 的子类,但它不共享相同的公共属性,并且对于相同的行为似乎有不同的方法。

标签: python class subclass


【解决方案1】:

正确使用子类化需要遵循以下替换原则:

如果有一些x_1 类型为T_1x_2 类型为T_2 的对象issubclass(T_2, T_1) == True,那么适用于x_1 的任何属性也必须适用于x_2

换句话说,您希望子类化实现行为,而不是改变现有行为。

在您的示例中,坐标系本身的更改是行为的更改,因此HexCell 不应继承自Cell

你可以做的是创建一个基类BaseCell,它封装了CellHexCell之间的共同行为并从它继承。

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):
        ...

【讨论】:

    【解决方案2】:

    您的 Cell 类实际上不是抽象的“Cell”,而是二维空间中的方形单元(正好有 4 个邻居,有“row”和“col”位置)。这样的单元格可能不会被十六进制单元格子类化,因为十六进制单元格只是一种不同类型的单元格:)

    正如您所注意到的,唯一常见的是 link() 方法和 links 属性。如果你坚持子类化,你可以创建类似的东西:

    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
    

    【讨论】:

    • 是的,这使它更像我从一开始就想要的那样。谢谢。
    猜你喜欢
    • 2011-05-27
    • 2015-08-14
    • 2017-06-07
    • 2022-10-02
    • 2021-01-12
    • 2015-03-03
    • 1970-01-01
    • 2010-11-19
    • 2017-08-20
    相关资源
    最近更新 更多