Re: [Tutor] Iterator vs. iterable cheatsheet, was Re: iter class
On 01/25/2014 04:13 AM, eryksun wrote: On Fri, Jan 24, 2014 at 8:50 AM, spir wrote: xs is an iterator (__next__ is there), then Python uses it directly, thus what is the point of __iter__ there? In any case, python must check whether Python doesn't check whether a type is already an iterator. It's simpler to require that iterators implement __iter__, like any other non-sequence iterable. This technically allows an iterator to return a new iterator when __iter__ is called: class C: def __iter__(self): return D() def __next__(self): return 'C' class D: def __iter__(self): return C() def __next__(self): return 'D' it1 = iter(C()) it2 = iter(it1) >>> next(it1) 'D' >>> next(it2) 'C' That said, it's supposed to return self. All right, thank you, Peter & Eryksun. d ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Iterator vs. iterable cheatsheet, was Re: iter class
On Fri, Jan 24, 2014 at 8:50 AM, spir wrote: > > xs is an iterator (__next__ is there), then Python uses it directly, thus > what is the point of __iter__ there? In any case, python must check whether Python doesn't check whether a type is already an iterator. It's simpler to require that iterators implement __iter__, like any other non-sequence iterable. This technically allows an iterator to return a new iterator when __iter__ is called: class C: def __iter__(self): return D() def __next__(self): return 'C' class D: def __iter__(self): return C() def __next__(self): return 'D' it1 = iter(C()) it2 = iter(it1) >>> next(it1) 'D' >>> next(it2) 'C' That said, it's supposed to return self. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Iterator vs. iterable cheatsheet, was Re: iter class
On 01/24/2014 06:44 PM, Peter Otten wrote: There is no infinite recursion. The for loop is currently implemented as # expect an iterable # handle iterators through an idempotent iter() tmp = iter(xs) # here you must check that tmp actually implements the iterator protocol, # else raise an error while True: try: x = next(tmp) except StopIteration: break # use x If I understand you correctly you suggest the following: # expect an iterator # fall back to getting an iterator through iter() try: tmp = xs.__next__ except AttributeError: tmp = iter(xs).__next__ while True: try: x = tmp() except StopIteration: break How is that simpler? see above d ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Iterator vs. iterable cheatsheet, was Re: iter class
spir wrote: > On 01/24/2014 10:22 AM, Peter Otten wrote: >> >> There's an odd outlier that I probably shouldn't tell you about [...] > > I guess there is a whole class of outliers; not really sure how to > classify them. I think you are focusing on the details too much. In > class Angles: > def __getitem__ (self, i): > return self.angles[i] * 360 / TAU > def __iter__ (self): > return iter(self.angles) iter(self.angles) is just an implementation detail. The important point is that __iter__() returns a new iterator on each invocation, thus making Angles an "iterable" according to the naming scheme I was trying to establish. The __getitem__() method can be ignored as it is used for element lookup only, not for iteration. > Side question: what is the point of __iter__ on iterators? Take a 'for' > loop like: for x in xs: f(x) > In the case where xs is not an iterator (no __next__), python calls > iter(xs), which IIUC may call xs.__iter__() unless it is a builtin. But if > xs is an iterator (__next__ is there), then Python uses it directly, thus > what is the point of __iter__ there? In any case, python must check > whether xs is an iterator (__next__). So there is no sense in calling > __iter__ on an iterator. Logically, this would lead to an infinite > recursion (calling __iter__ again and again). But python does it anyway > (and stops after the first call, indeed): There is no infinite recursion. The for loop is currently implemented as # expect an iterable # handle iterators through an idempotent iter() tmp = iter(xs) while True: try: x = next(tmp) except StopIteration: break # use x If I understand you correctly you suggest the following: # expect an iterator # fall back to getting an iterator through iter() try: tmp = xs.__next__ except AttributeError: tmp = iter(xs).__next__ while True: try: x = tmp() except StopIteration: break How is that simpler? > The only theoretical case I can find is iterators which do implement the > protocol (__next__) but are not to be used (!), instead delegate to > another iterator. Again: the important differentiation is between iterator and iterable, not how the iterator is implemented. > Then, why do they bear __next__ at all? why are they > iterators at all? Python allows you to do things that make no sense from the point of view of a human being. You can also implement an integer type with a negative abs(): >>> class Int(int): ... def __abs__(self): return self ... >>> abs(Int(-1)) -1 My advice: don't do it unless you have a good reason. ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
Re: [Tutor] Iterator vs. iterable cheatsheet, was Re: iter class
On 01/24/2014 10:22 AM, Peter Otten wrote: There's an odd outlier that I probably shouldn't tell you about [...] I guess there is a whole class of outliers; not really sure how to classify them. This is the case of defining a wrapper or "proxy" type, for a underlying data structure which is iterable, typically a builtin collection. This was evoked (but not specifically termed as "wrapper" or such) in previous message of the orginal thread. In that case, __iter__ would neither return self (it is not an iterator), nore a hand-baked iterator, but the builtin (or already defined) one of the underlying iterable. Two example, rather artificial (but code works): First, say you have an array of angles, which for the user are measured in degrees but internally use radians. (A better example may be of internal machine-friendly RGB colors and external user-friendly HSL colors [not HSV! g...].) At the interface, there is conversion. Iteration actually is iterating on the internal array, thus just uses iter(). import math ; TAU = 2 * math.pi class Angles: def __init__ (self): self.angles = list() def put (self, angle): self.angles.append(angle * TAU / 360) def __getitem__ (self, i): return self.angles[i] * 360 / TAU def __iter__ (self): return iter(self.angles) angles = Angles() angles.put(100) ; angles.put(200) ; angles.put(300) print(angles[1]) for a in angles: print(a) Second, we build an associative array (for few entries) as a plain association list à la Lisp, but implemented as a pair of lists instead as a list of pairs (this saves an intermediate notion of Pair). Iterating is here on the pair of list, zipped (in the python sense) together: class AssList: def __init__ (self): self.keys, self.vals = list(), list() def put (self, key, val): self.keys.append(key) self.vals.append(val) def __getitem__ (self, i): return self.keys[i], self.vals[i] def __iter__ (self): return iter(zip(self.keys, self.vals)) al = AssList() al.put(1,'a') ; al.put(2,'b') ; al.put(3,'c') print(al[1]) for k,v in al: print(k,v) Side question: what is the point of __iter__ on iterators? Take a 'for' loop like: for x in xs: f(x) In the case where xs is not an iterator (no __next__), python calls iter(xs), which IIUC may call xs.__iter__() unless it is a builtin. But if xs is an iterator (__next__ is there), then Python uses it directly, thus what is the point of __iter__ there? In any case, python must check whether xs is an iterator (__next__). So there is no sense in calling __iter__ on an iterator. Logically, this would lead to an infinite recursion (calling __iter__ again and again). But python does it anyway (and stops after the first call, indeed): class Iter: def __init__ (self): self.n = 0 def __next__ (self): if self.n == 10: raise StopIteration self.n += 1 return self.n def __iter__ (self): print("*** __iter__ ***") # * return self it = Iter() for i in it: print(i, end=" ") print() huh? The only theoretical case I can find is iterators which do implement the protocol (__next__) but are not to be used (!), instead delegate to another iterator. Then, why do they bear __next__ at all? why are they iterators at all? denis ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor
[Tutor] Iterator vs. iterable cheatsheet, was Re: iter class
+--+++ | | __iter__ | __next__ | +--+++ | iterable | return an iterator | not available | +--+++ | iterator | return self| return next item | | || or raise StopIteration | +--+++ iter(x) is x --> True: x is an iterator False: x is an iterable raises Exception: x is neither iterator nor iterable Once next(it) raises a StopIteration on a well-behaved iterator it must continue to raise `StopIteration`s on subsequent next(it) calls. There's an odd outlier that I probably shouldn't tell you about: classes with a __getitem__() method behave like iterators. An eventual IndexError exception is propagated as StopIteration: >>> class A: ... def __getitem__(self, index): ... if index > 2: ... raise IndexError ... return index * index ... >>> for item in A(): ... print(item) ... 0 1 4 ___ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor