Jia-Yu Aw wrote: > How to use __iter__ > Von: > Jia-Yu Aw <jiay...@yahoo.com.sg> > Antwortadresse: > Jia-Yu Aw <jiay...@yahoo.com.sg> > Datum: > Dienstag, 9. August 2011 03:48:20 > An: > tutor@python.org <tutor@python.org> > Gruppen: > gmane.comp.python.tutor > Hi all > > I've been learning Classes and have read the documentation on __iter__ but still don't understand how it works. I have two pieces of code: the first is def __iter__, the second is using __iter__. Instead of being an iterator, I'm just using this is a function, which I think is incorrect. Can somebody explain __iter__ and, if it's related, next()?
An "iterable" is an object that has an with an __iter__() method. The __iter__() method is supposed to return an "iterator", i. e. an object with a next() method. The next() method is then called repeatedly until a next() call raises a StopIteration. You can think of for item in items: do_something_with(item) as syntactic sugar for it = items.__iter__() while True: try: item = it.next() except StopIteration: break do_something_with(item) With that in mind > This is the first. But unlike what it says in the triple quotes, I return a whole list instead of a single shape: > > def __iter__(self): > """ > Return an iterator that allows you to iterate over the set of > shapes, one shape at a time > """ > import copy > setCopy = copy.copy(self.set) > shapeList = [] > while len(setCopy) > 0: > try: > y = setCopy[0] > shapeList.append(y) > setCopy.remove(y) > except StopIteration: > break > return shapeList your __iter__() method would become class MyIter(object): def __init__(self, items): self.items = list(items) def next(self): if len(self.items) == 0: raise StopIteration return self.items.pop(0) class MyIterable(object): # ... def __iter__(self): return MyIter(self.set) However, this is a clumsy way to implement an iterable. One alternative takes advantage of the fact that Python already knows how to iterate over a set. class MyIterable(object): def __iter__(self): for item in self.set: yield item Here the "yield" statement turns __iter__() from a function into a "generator". Every call of a generator creates a new iterator, i. e. an object with a next() method. Every next() call proceeds execution of the generator's body until it reaches a yield. Once the end of the body is reached next() will raise Stopiterations forever: >>> def f(): ... yield 1 ... yield 2 ... >>> g = f() >>> g.next() 1 >>> g.next() 2 >>> g.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> g.next() Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration A final simplification would be class MyIterable(object): def __iter__(self): return iter(self.set) > The second, how I've used __iter__: > > def findLargest(shapes): > """ > Returns a tuple containing the elements of ShapeSet with the > largest area. > shapes: ShapeSet > """ > ## TO DO > > areaList = [] > areaDict = {} > largestDict = {} Don't do that: > shapeList = shapes.__iter__() > for s in shapeList: The for-loop implicitly invokes the __iter__() method. Just loop over the original object: for s in shapes: > areaDict[s] = s.area() > areaList.append(s.area()) > > largestArea = max(areaList) > areaList = [] #re-initialize areaList, cleaning it > > import copy > dictCopy = copy.copy(areaDict) > for a in areaDict: > if areaDict[a] != largestArea: > del dictCopy[a] > > return tuple(dictCopy) Side note: you are copying data way to much. Don't! Most occurences of copy() only serve to help an experienced Pythonista identify newbie code ;) As an inspiration here's an alternative implementation: def find_largest(shapes): largest_area = max(shape.area() for shape in shapes) return [shape for shape in shapes if shape.area() == largest_area] _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: http://mail.python.org/mailman/listinfo/tutor