In the case of itertools.repeat
(and most iterators), using a proper class implementing the iterator
protocol has a few advantages from the implementation / maintenance POV - like you can have better control of the iteration, you can specialize the class etc. I also suspect that there are some optimisations that can be done at C-level for proper iterators that don't apply to generators.
Also remember that classes and functions are objects too - the def
statement is mostly syntactic sugar for creating a function
instance and populating it with compiled code, local namespace, cells, closures and whatnots (a somehow involved task FWIW, I did once just for out of curiousity and it was a major PITA), and the class
statement is also syntactic sugar for creating a new type
instance (doing it manually happens to be really trivial actually). From this POV, yield
is a similar syntactic sugar that turns your function into a factory returning instances of the generic generator
builtin type - IOW it makes your function act like a class, without the hassle of writing a full-blown class but also without the fine control and possible optimisations you can get by writing a full-blown class.
On a more general leval, sometimes writing your "function" as a custom callable type instead offers similar gains - fine control, possible optimisations, and well sometimes just better readability (think of two-steps decorators, custom descriptors etc).
Finally wrt/ builtin types (int
, str
etc) IIRC (please someone correct me if i'm wrong) they originally were functions acting as factory functions (before the new-style classes revolution when builtin types and user-defined types were different kind of objects). It of course makes sense to have them as plain classes now, but they had to keep the all_lower naming scheme for compatibility.