gpt4 book ai didi

python - __iter__ 和 __getitem__ 有什么区别?

转载 作者:太空狗 更新时间:2023-10-29 17:13:22 25 4
gpt4 key购买 nike

对我来说,这发生在 Python 2.7.6 和 3.3.3 中。当我这样定义一个类时

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

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

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

它只是将 args 加一并永远打印 None。就语言设计而言,这是故意的吗?

样本输出

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

最佳答案

是的,这是有意设计的。它被记录在案、经过充分测试,并被诸如 str 之类的序列类型所依赖。

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

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

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

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

>>> 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]

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

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


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

有兴趣了解细节的,相关代码在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;
}

在 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;
}

关于python - __iter__ 和 __getitem__ 有什么区别?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20551042/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com