Using iter on an existing iterator in Python: What happens to the iterator?
iterator, python
Solution
TLDNR: `iter` returns `obj.__iter_`. It doesn't return `obj` "as is".
The Cpython implementation of `iter` is pretty straightforward:
PyObject *
PyObject_GetIter(PyObject *o)
{
PyTypeObject *t = o->ob_type;
getiterfunc f = NULL;
if (PyType_HasFeature(t, Py_TPFLAGS_HAVE_ITER))
f = t->tp_iter; // <- if it has __iter__, return that
....more stuff
So when you call `iter(obj)` and `obj.__iter__` exists it just returns that. Most (all?) built-in iterators have `__iter__ = self`, e.g.
PyTypeObject PyListIter_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"listiterator", /* tp_name */
....
PyObject_SelfIter, /* tp_iter */
....
but that's not necessary true for userland objects:
class X:
def __iter__(self):
return Y()
class Y:
def __iter__(self):
return iter('xyz')
a = iter(X())
b = iter(a)
print a is b # False
Problem
The iter function wraps objects like lists or tuples in order to use them as iterators, i.e. one is able to use `next`, for example. For instance, ``` next(iter([1, 2, 3])) ``` returns 1. What happens internally if the object we pass to `iter` is already an iterator? Does it simply return the original object, i.e. no-op? Or does it produce a new iterator wrapping the original one? And by wrapping I don't mean copying the original iterator, of course.