父类“枚举”如何访问未设置为实例变量的参数?

2024-05-20 19:35:07 发布

您现在位置:Python中文网/ 问答频道 /正文

昨天我偶然发现了一个question,它涉及对iterable类型进行枚举,并在iterable中生成降序索引和升序项。你知道吗

在:

letters = ['a', 'b', 'c']
for i, letter in revenumerate(letters):
    print('{}, {}'.format(i, letter))

输出:

2, a
1, b
0, c 

我决定尝试创建一个enumerate子类来重新定义__iter____next__方法,而不是使用内置的reverse两次或简单地i = len(letters) - i - 1编写一个快速可靠的答案。你知道吗

我最初的工作方案代码如下:

class revenumerate(enumerate):
    def __init__(self, iterable, start=0):
        self._len = len(iterable)
        self._start = start
        if isinstance(iterable, dict):
            self._data = iterable.keys()
        else:
            self._data = iterable

    def __iter__(self):
        _i = self._len
        for _item in self._data:
            _i -= 1
            _ind = _i + self._start
            yield _ind, _item

    def __next__(self):
        _i, _item = super().__next__()
        _ind = self._len +  2 * self._start - _i - 1
        return _ind, _item

但是,我现在意识到这段代码有冗余,因为enumerate.__iter__似乎产生了__next__的结果,这是有意义的。删除重新定义的__iter__之后,我意识到self._data没有在任何地方使用,所以我删除了__init__的最后四行,留下了下面的代码,它仍然提供了所需的行为。你知道吗

class revenumerate(enumerate):
    def __init__(self, iterable, start=0):
        self._len = len(iterable)
        self._start = start

    def __next__(self):
        _i, _item = super().__next__()
        _ind = self._len +  2 * self._start - _i - 1
        return _ind, _item

现在看来,传递到revenumerate的iterable参数除了确定整数self._len之外,什么都不是。你知道吗

我的问题是iterable存储在哪里,super().__next__如何访问它?

用PyCharm调试器快速查看一下builtins.py并不能提供很多帮助来弄清楚这一点(在这个阶段我似乎是这样认为的),而且我对Python源代码库没有很好的了解。我的猜测与父类enumerate__new____init__方法有关,或者它是父类object。你知道吗


Tags: 代码selfdataleninitdefiterableitem
3条回答

enumerate所做的或多或少是这样的:

def enumerate(iterable):
    counter = 0
    for item in iterable:
        counter += 1
        yield counter, item

你可以注意到的一件事是它不知道iterable有多长。它甚至可以无限长,但enumerate仍然有效。你知道吗

revenumerate的问题是,在能够生成第一个项目之前,首先必须计算有多少个项目,因此实际上必须创建一个所有枚举项目的列表,然后向后生成它们(至少如果您希望您的revenumerate处理任何iterable,如enumerate)。你知道吗

一旦你承认这个限制是不可撤销的,剩下的就很简单了:

def revenumerate(iterable):
    all_items = list(iterable)
    counter = len(all_items)
    for item in reversed(all_items):
        counter -= 1
        yield counter, item

(*)enumerate实际上是一个类,但这是它的行为。参见my other answer了解它的工作原理以及__next__的作用。你知道吗

其他人已经回答了您关于代码如何工作的特定问题,因此下面是使用zip()实现反向枚举器的另一种方法:

def revenumerate(iterable, start=None):
    if start is None:
        start = len(iterable) - 1
    return zip(range(start, -1, -1), iterable)

>>> revenumerate('abcdefg')
<zip object at 0x7f9a5746ec48>
>>> list(revenumerate('abcdefg'))
[(6, 'a'), (5, 'b'), (4, 'c'), (3, 'd'), (2, 'e'), (1, 'f'), (0, 'g')]
>>> list(revenumerate('abcdefg', 100))
[(100, 'a'), (99, 'b'), (98, 'c'), (97, 'd'), (96, 'e'), (95, 'f'), (94, 'g')]

revenumerate()返回一个zip对象,它与enumerate()返回的enumerate对象非常相似。你知道吗

默认情况下,项将从iterable less的长度开始枚举,这要求长度是有限的。您可以提供一个开始值,从该值开始倒计时,如果您只想从任意值开始计数,或者处理无限多个iterables,这将非常有用。你知道吗

>>> from itertools import count
>>> g = revenumerate(count(), 1000)
>>> next(g)
(1000, 0)
>>> next(g)
(999, 1)
>>> next(g)
(998, 2)
>>> next(g)
(997, 3)
>>> next(g)
(996, 4)

如果您试图在不指定起始值的情况下处理无限iterable:

>>>> revenumerate(count())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in revenumerate
TypeError: object of type 'itertools.count' has no len()

防止解释器进入无限循环。如果适合您的应用程序,您可以处理异常并引发自己的异常。你知道吗

builtins.py是个谎言。皮查姆编的。如果您想查看builtins模块的真正源代码,那就是pythongit存储库中的^{}enumerate本身在^{}中实现。你知道吗

enumerate迭代器将迭代器存储在C级en_sit结构槽中的基础对象上:

typedef struct {
    PyObject_HEAD
    Py_ssize_t en_index;           /* current index of enumeration */
    PyObject* en_sit;          /* secondary iterator of enumeration */
    PyObject* en_result;           /* result tuple  */
    PyObject* en_longindex;        /* index for sequences >= PY_SSIZE_T_MAX */
} enumobject;

enumerate.__new__中设置:

static PyObject *
enum_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
    ...
    en->en_sit = PyObject_GetIter(seq);

它是在__new__中设置的,这就是为什么即使您忘记调用super().__init__,它仍然可以工作的原因。你知道吗


为此对enumerate进行子类化没有多大意义。enumerate只记录为可调用的;它是一个类并支持子类化这一事实是一个实现细节。而且,你并没有从enumerate中得到太多的使用,你的迭代器和enumerate迭代器之间的关系听起来并不像是“is-a”。像zvone那样,将您的功能实现为一个生成器,更加清晰明了。你知道吗

相关问题 更多 >