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! grrrr...].) 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

Reply via email to