On Thu, Jan 23, 2014 at 02:24:20PM -0500, Keith Winston wrote: > On Thu, Jan 23, 2014 at 7:05 AM, eryksun <eryk...@gmail.com> wrote: > > Generally you'll make `__iter__` a generator, so you don't have to > > worry about implementing `__next__`. Also, the built-in function > > `next` was added in 2.6, so you don't have to worry about the method > > name difference between 2.x and 3.x, either. > > I'm now realizing I don't understand this comment at all. First, there > IS a __iter__ method in the example: does the presence of such make it > a generator already, or is it a usage thing, or something else? I > don't yet completely understand the difference/point... or maybe you > mean inherit the generator class?
Generators are a kind of function, which are special. You can't inherit from them: py> def generator(): ... yield 1 ... py> class MyGenerator(type(generator)): ... pass ... Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: type 'function' is not an acceptable base type although I expect that should probably be considered an implementation detail of CPython rather than a language features. (Some other Pythons may allow you to inherit from function, if they wanted to.) Eryksun means that when you write an iterable class, you might be tempted to manage the next method yourself, but nine times out of ten you don't need to, you can delegate the annoying part of the coding to Python. That is, instead of creating an *actual* iterator: # Pseudocode class MyIterator: def __iter__(self): return self def __next__(self): # I have to manage this myself... if there still are items to process: return the next item else: raise StopIteration you can *return* an iterator. This makes your class a mock or pseudo-iterator, I suppose, although for the most part only pedants normally worry about the difference and we normally call it an iterator and hope nobody gets confused. But for now, I'm going to be pedantic and be absolutely firm on the difference: class MyPseudoIterator: def __iter__(self): return iter(some data) (It's not a true iterator, because it doesn't return self. Instead, it returns a true iterator. But 95% of the time, we don't care too much about the difference.) Sometimes you already have a bunch of data ready to use, like a list, and calling iter() is easy. But sometimes you want to calculate the data on the fly, and that's where Eryksun's suggestion to use a generator is specially useful: class MyPseudoIterator: def __iter__(self): # use "yield" instead of "return" yield "something" yield "another thing" result = calculate_stuff() yield result Notice that in both cases I don't declare a __next__ method! That's because I never call next() on MyPseudoIterator instances directly, I always call iter() first: # this won't work instance = MyPseudoIterator() result = next(instance) # fails # this does instance = MyPseudoIterator() it = iter(instance) result = next(it) # works For-loops are okay since Python automatically calls iter for you: instance = MyPseudoIterator() for item in instance: ... > Second: you state that I don't have to worry about the name > difference, but when I changed the method name from next to __next__ > it worked in 3.3. So what's your point here? I think that Eryksun means that in Python 2, you can call: it.next() but in Python 3 you can call: it.__next__() but neither are recommended, instead you should call: next(it) and let the built-in next() function worry about the difference. -- Steven _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor