I think it's also worth noting that a missing "`__iter__` that returns self" is trivial to recover from... just use a new reference to the iterator instead. The overhead of a method call for this convention almost seems silly.
The use case is this:
def foo(it):
for x in it:
print(x)
def main():
it = iter([1, 2, 3])
next(it)
foo(it)
Since "for x in it" calls iter(it), if the argument is an iterator that doesn't define __iter__, it would fail. But this is all about convention -- we want to make it convenient to do this kind of thing, so all standard iterators define __iter__ as well as __next__.
What worries me most about changing the current "requirement" is that it may create either confusion or backward compatibility issues for `collections.abc.Iterator` (which is a subtype of `Iterable`, and thus requires `__iter__`).
If you explicitly inherit from Iterator, you inherit a default implementation of __iter__ (that returns self, of course). If you merely register, it's up to you to comply. And sometimes people register things that don't follow the letter of the protocol, just to get things going. (This is common for complex protocols like Mapping, where some function you have no control over insists on a Mapping but only calls one or two common methods.
Duck typing is alive and kicking!