__iter__ 和 __getitem__ 有什么区别? [英] What's the difference between __iter__ and __getitem__?

查看:58
本文介绍了__iter__ 和 __getitem__ 有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这发生在 Python 2.7.6 和 3.3.3 中.当我定义这样的类时

class foo:def __getitem__(self, *args):打印(*参数)

然后尝试在一个实例上迭代(以及我认为会称为 iter 的),

bar = foo()对于我在酒吧:打印(一)

它只是为 args 加一并永远打印 None .就语言设计而言,这是有意为之吗?

样本输出

<代码>0没有任何1没有任何2没有任何3没有任何4没有任何5没有任何6没有任何7没有任何8没有任何9没有任何10没有任何

解决方案

是的,这是一个预期的设计.它被记录在案,经过充分测试,并被 str 等序列类型所依赖.

__getitem__ 版本是 Python 拥有现代迭代器之前的遗留版本.这个想法是任何序列(可索引并具有长度的东西)都可以使用系列 s[0], s[1], s[2], ... 自动迭代,直到 IndexErrorStopIteration 被引发.

例如,在 Python 2.7 中,由于 __getitem__ 方法(​​str 类型没有 __iter__ 方法),字符串是可迭代的.

相比之下,迭代器协议让任何类都可以迭代,而不必可索引(例如字典和集合).

以下是如何使用序列的遗留样式创建可迭代类:

<预><代码>>>>A类:def __getitem__(self, index):如果索引 >= 10:引发索引错误返回索引 * 111>>>列表(A())[0, 111, 222, 333, 444, 555, 666, 777, 888, 999]

以下是使用 __iter__ 方法创建可迭代对象的方法:

<预><代码>>>>B类:def __iter__(self):产量 10产量 20产量 30>>>列表(B())[10, 20, 30]

对细节感兴趣的朋友,相关代码在Objects/iterobject.c:

静态 PyObject *iter_iternext(PyObject *iterator){seqiterobject *it;PyObject *seq;PyObject *结果;断言(PySeqIter_Check(迭代器));it = (seqiterobject *)iterator;seq = it->it_seq;如果(seq == NULL)返回空;结果 = PySequence_GetItem(seq, it->it_index);如果(结果!= NULL){it->it_index++;返回结果;}如果(PyErr_ExceptionMatches(PyExc_IndexError)||PyErr_ExceptionMatches(PyExc_StopIteration)){PyErr_Clear();Py_DECREF(seq);it->it_seq = NULL;}返回空;}

在 Objects/abstract.c 中:

intPySequence_Check(PyObject *s){如果 (s == NULL)返回0;如果(PyInstance_Check(s))return PyObject_HasAttrString(s, "__getitem__");如果(PyDict_Check(s))返回0;返回 s->ob_type->tp_as_sequence &&s->ob_type->tp_as_sequence->sq_item != NULL;}

This happens in Python 2.7.6 and 3.3.3 for me. When I define a class like this

class foo:
    def __getitem__(self, *args):
        print(*args)

And then try to iterate (and what I thought would call iter) on an instance,

bar = foo()
for i in bar:
    print(i)

it just counts up by one for the args and prints None forever. Is this intentional as far as the language design is concerned?

Sample output

0
None
1
None
2
None
3
None
4
None
5
None
6
None
7
None
8
None
9
None
10
None

解决方案

Yes, this is an intended design. It is documented, well-tested, and relied upon by sequence types such as str.

The __getitem__ version is a legacy before Python had modern iterators. The idea was that any sequence (something that is indexable and has a length) would be automatically iterable using the series s[0], s[1], s[2], ... until IndexError or StopIteration is raised.

In Python 2.7 for example, strings are iterable because of the __getitem__ method (the str type does not have an __iter__ method).

In contrast, the iterator protocol lets any class be iterable without necessarily being indexable (dicts and sets for example).

Here is how to make an iterable class using the legacy style for sequences:

>>> class A:
        def __getitem__(self, index):
            if index >= 10:
                raise IndexError
            return index * 111

>>> list(A())
[0, 111, 222, 333, 444, 555, 666, 777, 888, 999]

Here is how to make an iterable using the __iter__ approach:

>>> class B:
        def __iter__(self):
            yield 10
            yield 20
            yield 30


>>> list(B())
[10, 20, 30]

For those who are interested in the details, the relevant code is in Objects/iterobject.c:

static PyObject *
iter_iternext(PyObject *iterator)
{
    seqiterobject *it;
    PyObject *seq;
    PyObject *result;

    assert(PySeqIter_Check(iterator));
    it = (seqiterobject *)iterator;
    seq = it->it_seq;
    if (seq == NULL)
        return NULL;

    result = PySequence_GetItem(seq, it->it_index);
    if (result != NULL) {
        it->it_index++;
        return result;
    }
    if (PyErr_ExceptionMatches(PyExc_IndexError) ||
        PyErr_ExceptionMatches(PyExc_StopIteration))
    {
        PyErr_Clear();
        Py_DECREF(seq);
        it->it_seq = NULL;
    }
    return NULL;
}

and in Objects/abstract.c:

int
PySequence_Check(PyObject *s)
{
    if (s == NULL)
        return 0;
    if (PyInstance_Check(s))
        return PyObject_HasAttrString(s, "__getitem__");
    if (PyDict_Check(s))
        return 0;
    return  s->ob_type->tp_as_sequence &&
        s->ob_type->tp_as_sequence->sq_item != NULL;
}

这篇关于__iter__ 和 __getitem__ 有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆