如何为派生 class 的每个实例设置基础 class?

How to set base class per instance of derived class?

遇到问题我遇到过很多次,解决方案往往是循环列表。例如:

my_list = CircularList([1,2,3,4])
my_list[0] # 1
my_list[4] # 1

为了实现这样的构造,我这样做了:

class CircularList(list):
    def __getitem__(self, index: int):
        return list.__getitem__(self, index % len(self))

在实现 class 之后,我想制作一个 class 可以“Circularize”任何实现 __getitem____len___,同时仍然保留该对象的所有属性和方法。 例如:

circ_iter = Circular(MyIter(1,2,3,4))   # Object keeps MyIters's attributes
circ_tuple = Ciruclar((1,2,3,4))  # Still has attributes of a tuple

我 运行 遇到的问题是我找不到一种方法来根据参数的类型为每个实例动态设置基础 class。

我应该怎么做?

您可以通过混合 class 通过多重继承来做到这一点,但您必须确保两件事:

  1. 没有其他 class 覆盖 __getitem__ 出现在 before Circularize 基础列表 classes
  2. 您自己的 class 不会覆盖 __getitem__

(从技术上讲,您不能以阻止 Circularize.__getitem__ 按预期工作的方式覆盖 __getitem__。但这涉及到继承如何与可组合性冲突的问题,而不是真正在此答案的范围内。)

class Circularize:
    def __getitem__(self, index):
        return super().__getitem__(index % len(self))


_circular_types = {}
def Circular(v: collections.abc.Sequence):
    t = type(v)
    tname = t.__name__
    if tname not in _circular_types:
        _circular_types[tname] = type(f'Circular{tname.upper()}', (Circularize, t), {})

    return _circular_types[tname](v)
    

circ_list = Circular([1,2,3])  # Creates an instance of CircularList
circ_tuple = Circular((1,2,3))  # Creates an instance of CircularTuple

我省略了 CircularSet 因为集合本身是不可索引的(即 set 不是 Sequence 的子 class)。如果要重复迭代集合的元素,请使用 itertools.cycle({1,2,3})(产生 1, 2, 3, 1, 2, 3, 1, 2, ...)。