从列表继承时,迭代器表现良好,但从双端队列继承时表现不佳 - Python

Iterator behaves well when subclassing from list, but not from deque - Python

我想创建一个“循环列表”对象:我可以通过它循环地、永远地迭代。为此,我尝试将 class 子 list class 如下:

from itertools import cycle 

class Circle(list):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def __iter__(self):
        sup_iter = super().__iter__()
        return cycle(sup_iter)

而且,确实,它的效果非常好(在某种程度上。无法将其转换回 list,正如某些答案所指出的那样)。但是,如果我尝试将我的 class 基于 deque class,我似乎再也无法 在我的对象上调用我的 strrepr 因为这样做会使 python 解释器冻结,并且该进程最终会被杀死。请记住,当 class 继承自 list.

时,会发生 none

我很茫然,任何人都可以帮助阐明正在发生的事情吗?

当然不能在无限迭代器上调用 list。迭代器上 list 的定义是它不断读取元素,直到迭代器告诉它没有更多元素为止。

你知道itertools.cycle吗?

将它们转换为列表并调用 repr(which will try to convert it to a list 在构建 repr 字符串的过程中)将尝试迭代双端队列。

但是您通过使迭代永无止境来“破坏”了这一点。

有人可能会说执行此操作的代码(最终,here)可以查看对象的长度(如果它有一个,不是所有的都会)并在那么多元素之后停止,但它没有:

    /* Run iterator to exhaustion. */
    for (;;) {
        PyObject *item = iternext(it);
        ...

IMO,你最好离开 __iter__ as-is 并使用 cycle(your_circle) 来迭代你的循环列表。

CPython 中的列表 repr 在这里定义:https://github.com/python/cpython/blob/v3.10.3/Objects/listobject.c#L361-L415

重点是这个循环使用了

for (i = 0; i < Py_SIZE(v); ++i) { ...

并且您根本没有覆盖对象的大小,因此就列表 repr 而言,Circle([1,2,3]) 仍然有 3 个元素。循环对每个元素执行一次 repr,然后结束。

CPython中的deque repr定义在这里:https://github.com/python/cpython/blob/v3.10.3/Modules/_collectionsmodule.c#L1376-L1404

重要的一点是这段代码使用了

aslist = PySequence_List(deque);

即它首先将双端队列表示为一个列表,对于您的 Circle class 来说,这将是一个无限循环,最终会耗尽所有可用内存并似乎冻结您的计算机。