Класс str_iterator
для str
(прогон type(iter("str"))
) был реализован в CPython3. Объект, который возвращается при вызове "str".__iter__()
или iter("str")
. В CPython2 собственный класс итератора для str
не был реализован. Когда вы вызываете iter("str")
, вы получаете экземпляр базового класса итератора.
См. 1 часть кода. f = t->tp_iter;
- вернуть пользовательский итератор, если он существует. В противном случае PySeqIter_New(o)
- вернуть экземпляр структуры seqiterobject
(см. 2 фрагмента кода). Как видно из 3 фрагментов кода, этот базовый итератор вызывает итерацию PySequence_GetItem(seq, it->it_index);
.
Если вы реализуете свой класс, поддерживающий итерацию, то вам нужно определить либо метод __iter__
, либо __getitem__
. Если вы выберете первую опцию, то в возвращаемом объекте должны быть реализованы методы __iter__
и __next__
(CPython3) или next
(CPython2).
Плохая идея проверить на hasattr(entity, "__iter__")
.
- Если вам нужно проверить, является ли объект повторяемым, запустите
isinstance(entity, Iterable)
. - Если вам нужно исключить только
str
, запустите isinstance(entity, Iterable) and not isinstance(entity, str)
(CPython3).
1
PyObject *
PyObject_GetIter(PyObject *o)
{
PyTypeObject *t = o->ob_type;
getiterfunc f;
f = t->tp_iter;
if (f == NULL) {
if (PySequence_Check(o)) return PySeqIter_New(o);
return type_error("'%.200s' object is not iterable", o);
}
else {
...
}
}
2
typedef struct {
PyObject_HEAD
Py_ssize_t it_index;
PyObject *it_seq; /* Set to NULL when iterator is exhausted */
} seqiterobject;
PyObject *
PySeqIter_New(PyObject *seq)
{
seqiterobject *it;
if (!PySequence_Check(seq)) {
PyErr_BadInternalCall();
return NULL;
}
it = PyObject_GC_New(seqiterobject, &PySeqIter_Type);
if (it == NULL)
return NULL;
it->it_index = 0;
Py_INCREF(seq);
it->it_seq = seq;
_PyObject_GC_TRACK(it);
return (PyObject *)it;
}
3
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;
if (it->it_index == PY_SSIZE_T_MAX) {
PyErr_SetString(PyExc_OverflowError, "iter index too large");
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();
it->it_seq = NULL;
Py_DECREF(seq);
}
return NULL;
}