[ list(squares( )[ make new iterable object new object for each new __iter__ call to support multiple iterations more directlywe could also recode this example with an extra class or other techniqueas we will in moment as isthoughby creating new instance for each iterationyou get fresh copy of iteration state in squares( true abc squares( abc ( ':join(map(strsquares( ))' : : : : other iteration contexts each calls __iter__ and then __next__ just like single-scan built-ins such as mapconverting to list supports multiple scans as wellbut adds time and space performance costswhich may or may not be significant to given programx squares( tuple( )tuple( (( )()iterator exhausted in second tuple( list(squares( )tuple( )tuple( (( )( )we'll improve this to support multiple scans more directly aheadafter bit of compareand-contrast classes versus generators notice that the preceding example would probably be simpler if it was coded with generator functions or expressions--tools introduced in that automatically produce iterable objects and retain local variable state between iterationsdef gsquares(startstop)for in range(startstop )yield * for in gsquares( )print(iend=' for in ( * for in range( ))print(iend=' unlike classesgenerator functions and expressions implicitly save their state and create the methods required to conform to the iteration protocol--with obvious advantages operator overloading