【问题标题】:How to set the "base class list" in derived class __init__ in Python?如何在 Python 的派生类 __init__ 中设置“基类列表”?
【发布时间】:2026-02-06 11:05:02
【问题描述】:

我有一个扩展列表的类;尽可能简化,看起来像:

class grid(list):
   def __init__(self, data):
       "If data is an int, create empty grid of size data; if data is a list, initialize grid with that data."
       if type(data) == grid:   # make a new copy of the old grid instance 'data'
           self += data
           self.__dict__ |= data.__dict__.copy()
       elif type(data) == list:
           self += data
           self.score = sum(len(row) for row in data)
       elif type(data) == int:
           self += [[] for _ in range(data)]  # NOTE: [[]]*data creates multiple references to the same empty row!
           self.score = 0
       else: raise Exception("data must be integer or list!")

它有效,但我觉得很尴尬,我必须(?)使用+= 将我想要创建的内容“附加”到最初为空的“基本列表”: 如果使用,例如self = data,那么self 将成为一个基本列表,并且设置像“self.score = ...”这样的属性会导致错误。

我认为和/或知道我也可以使用super().__init__(data) 将给定的初始数据复制到基础列表中。但它看起来有点矫枉过正,不必要地“沉重”。 (我的做法不是(很多)更有效率吗?毕竟空列表已经存在,super().__init__ 不会创建一个全新的列表吗?)对于 data=int 的情况,我应该说@ 987654327@ 创建 两个 新“空”列表的副本:作为参数提供给 super.init 的副本和由该函数创建的副本!?

另外,我用于创建grid 的现有实例的副本的代码,尤其是self__dict__ |= data.__dict__.copy(),是否可行?实际上,除了.score,还有更多的属性,其中一些是列表。我知道我必须(?)为每个人使用单独的copy()s 来有效地创建这些副本的新副本,而不仅仅是引用旧实例的属性。我应该使用一些deepcopy 模块以“更清洁”(?)的方式做到这一点吗?

编辑/更新:澄清一下,我的问题是是否有办法分配一个值到基本列表(在本例中,或在一般情况下为基本),如我们将值分配给对象的其他组件(即属性)(例如,self.__inner_list = list(data).)。 “行”(列表的元素)的类型无关紧要,为了简单起见,我在空网格中使用了 [] 但实际上行是我真实代码中的集合,但我也允许列表和字典 - 特别是因为我必须/want/do 使用空集的常用数学符号“{}”(但在 Python 中表示空的 dict)输出空行,并且为了保持一致性,还希望允许将其作为输入。从 cmets 看来,似乎没有这种方法可以“简单地”为基础对象分配一个值(毕竟这是合乎逻辑的)。

【问题讨论】:

  • 我不确定我是否会为此使用继承。虽然列表列表可用于表示一个网格,但真的所有列表列表都是网格吗?我可能会 1) 定义一个包含列表列表的类。 2) 让__init__ 将列表列表作为参数 3) 定义单独的类方法,这些方法将现有网格或 int 作为参数以返回新网格。 4) 定义对网格有意义的方法,而不是仅仅假设所有list 方法都是有效的。 (例如,你真的要允许g = grid(3); g.append("foo")吗?)
  • __init__ 不会创建列表;它初始化它。 list.__new__ 调用 type.__new__ 来实际创建新列表,该列表被传递给 list.__init__ 以将给定迭代中的值添加到其中。
  • 不,我的问题是,你认为[[1], [2,3,4,5], [6,7]] 是一个网格吗?当参数是整数时,您已经在创建一个空列表列表;我只是将它转移到一个单独的方法中,以避免使__init__ 复杂化,您希望它尽可能简单。 (理想情况下,初始化网格有一种“规范”方式,所有其他方式都可以按照规范方式实现;以我的回答为例。)
  • 使用super().__init__ 不会影响性能。这只是对内置方法的另一个调用,就像 self += ... 是对 self = self.__iadd__(...) 的调用一样。
  • 我不确切知道 如何 list.__init__ 工作,只是它做的一件事是复制任何可迭代的作为参数到新列表中。

标签: python oop inheritance


【解决方案1】:

忽略其他一些问题,我会这样写课程:

class grid(list):
   def __init__(self, lists: list[list]):
       self.extend([x] for x in lists)

   @classmethod
   def from_grid(cls, g: grid):
       # A grid is already a list of lists
       return cls(g)

   @classmethod
   def from_int(cls, n):
       return cls([[] for _ in range(n)])

list([1,2,3]) 这样的调用首先使用list.__new__ 创建一个空列表(忽略参数,尽管可能使用它为新列表预分配足够的空间以容纳三个值),然后传递该新列表和给定的可迭代到list.__init__ 以将可迭代的值复制到新列表中。您可以通过覆盖__init__ 来检查self 在调用super().__init__ 之前和之后的值:

class A(list):
    def __init__(self, x):
        print(self)
        super().__init__(x)
        print(self)

然后

>>> a = A([1,2,3])
[]
[1, 2, 3]

【讨论】:

  • 谢谢,但除非我错了,(1)你的 self.extend(...) 和我的 self += ... 没有什么不同; (2) list[list] 只是一个注解,在未来的版本中可能是真实的东西; (3) 列表和/或网格的类型/内容与我的问题并不真正相关;实际上我想允许 list[list] 和 dict,但会有效地将所有内容转换为 list[set] 的“真实”类型。我想知道是否有一种方法可以为基础列表分配一个值,就像我为其他属性分配值一样。似乎没有,应该使用 super().__init__ - 明白了!
  • extend+= 是相似的,但我也采取了额外的步骤来创建子列表的副本,因此您的 grid 的新实例不会与他原创。 (如果我不这样做,一个网格中子列表的更改会在另一个网格中可见。)
  • 我也可以改用super().__init__([list(x) for x in lists])
  • 这一切都没有实际意义,因为我认为无论如何你都应该使用组合而不是继承。 class grid: def __init__(self, lists: list[list]): self._data = [list(x) for x in lists].
  • 确实,在我的原版中,我确实使用了self += [ set(row) for row in data ]! :-)