__iter__和__getitem__之间有什么区别?

weg*_*gry 14 python python-2.7 python-3.x

对于我来说,这在Python 2.7.6和3.3.3中会发生.当我定义这样的类时

class foo:
    def __getitem__(self, *args):
        print(*args)
Run Code Online (Sandbox Code Playgroud)

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

bar = foo()
for i in bar:
    print(i)
Run Code Online (Sandbox Code Playgroud)

它只是为一个args计算,并永远打印无.就语言设计而言,这是故意的吗?

样本输出

0
None
1
None
2
None
3
None
4
None
5
None
6
None
7
None
8
None
9
None
10
None
Run Code Online (Sandbox Code Playgroud)

Ray*_*ger 24

是的,这是一个预期的设计.它经过记录,经过充分测试,并且依赖于序列类型,例如str.

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

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

相反,迭代器协议允许任何类可迭代,而不必是可索引的(例如,dicts和sets).

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

>>> 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]
Run Code Online (Sandbox Code Playgroud)

以下是使用__iter__方法进行迭代的方法:

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


>>> list(B())
[10, 20, 30]
Run Code Online (Sandbox Code Playgroud)

对于那些对细节感兴趣的人,相关的代码在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;
}
Run Code Online (Sandbox Code Playgroud)

在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;
}
Run Code Online (Sandbox Code Playgroud)


sme*_*eso 5

__iter__是迭代可迭代对象的首选方式。如果未定义,解释器将尝试使用__getitem__. 看看这里

  • **重要更正**:PEP 并未声明 \_\_iter\_\_ 优于 \_\_getitem\_\_;相反,它定义了两者,并简单地表示在回退到 \_\_getitem\_\_ 方法之前*先尝试* \_\_iter\_\_ 。PEP 的重点是为非序列对象添加迭代支持。我们并没有试图删除现有的序列支持或阻止其使用。 (2认同)
  • @Faust重要的是不要使用“首选”这个词——这错误地表明人们不应该使用 \_\_getitem\_\_ 方法。你是对的,有时 _\_iter\_\_ 可以有更好的性能。这就是为什么我实现了 *listiterator*,尽管它已经可以使用 \_\_getitem\_\_ 进行迭代。另一方面,使用 \_\_iter\_\_ 的 str/unicode 没有性能改进。这就是为什么我没有添加字符串迭代器。 (2认同)