[Tutor] __iter__ loops, partitioning list among children

Eric Abrahamsen eric at ericabrahamsen.net
Mon Aug 25 16:16:36 CEST 2008


Okay I think I'm onto something, more iterator-related stuff. If I can  
make self.events an iterator, then run a for loop on it, breaking out  
of the loop when the events' date attributes get too high. Then on the  
next run through, that same for loop should pick up where it left off,  
right? Here's my next version of the function. It doesn't quite work  
right: when I test it each child instance receives the correct events,  
but when it passes those events onto its children, they get consumed  
(or something) and are never actually output. To test I'm  
instantiating a Month m, filling it with events, then looping like so:

for w in m:
         print w.event_count()
         for d in w:
             print d.events

w.event_count() produces the right event count, but d.events is always  
empty. If anyone can see what's wrong with this function...

######

def _iter_children(self, child, require_events=False):
     """
     Iterate through an object's 'child' items.

     If require_events == True, only return children with
     events, otherwise return all children.
     """
     iterevents = iter(self.events)
     while self.sentinel < self.stop:
         c = child([], self.sentinel, self.start_attr,  
rolling=self.rolling, counts_only=self.counts_only)
         for e in iterevents:
             if getattr(e, self.start_attr) < c.stop:
                 c.events.append(e)
             else:
                 break
         self.sentinel += c.dt_range
         if not require_events or c.has_events():
             # if require_events == True, omit empty children.
             yield c



On Aug 25, 2008, at 11:49 AM, Eric Abrahamsen wrote:

>
> On Aug 24, 2008, at 7:20 PM, Kent Johnson wrote:
>
>> Forwarding to the list with my reply. Please use Reply All to reply  
>> to the list.
>
> Grr, sorry, I keep forgetting...
>
>>
>>
>> On Sun, Aug 24, 2008 at 1:02 AM, Eric Abrahamsen
>> <eric at ericabrahamsen.net> wrote:
>>>
>>> On Aug 23, 2008, at 11:22 PM, Kent Johnson wrote:
>>>
>>>> On Sat, Aug 23, 2008 at 6:47 AM, Eric Abrahamsen
>>>> <eric at ericabrahamsen.net> wrote:
>>>>>
>>>>> At first I thought the bisect module was the way to go, but it  
>>>>> is too
>>>>> tightly tied to integer list indices, and works very awkwardly  
>>>>> when
>>>>> bisecting on datetime attributes.
>>>>
>>>> I'm not sure what the problem is with bisect (to find the starting
>>>> point). Your list of model elements has integer indices. I think  
>>>> you
>>>> have to write a __cmp__ method for your model class that compares  
>>>> on
>>>> the datetime attribute, then it should work. You can also create a
>>>> list of (key, model) and sort/search that.
>>>> http://www.mail-archive.com/python-list@python.org/msg189443.html
>>>
>>> The __cmp__ trick is very nice, and would do it, except I won't  
>>> have access
>>> to the events model classes. I could get bisect to work by feeding  
>>> it a list
>>> comprehension, but making the high and low parameters work  
>>> required integer
>>> indices, which seemed like one half-hack too many...
>>
>> I don't understand the issue. *All* lists have integer indices. If  
>> you
>> make a list of (key, model) pairs, the keys only need to be
>> comparable, not integers.
>
> The main problem is that I don't have any control over the list of  
> models, and all I've got is the name of a datetime attribute to  
> filter by. The only way I could get bisect to work was like this:
>
> index = bisect([getattr(x, attr_name) for x in model_list],  
> sentinel_date)
>
> But that loops over the whole list, which is what I was trying to  
> avoid. And the only way I could provide high and low parameters to  
> bisect is by calling event_list.index(event), which doesn't work on  
> django querysets. I also experimented with itertools.groupby to  
> produce groups of events, but that turned out to be far slower than  
> simply looping over the whole event list and extracting events which  
> test true for c.start <= getattr(event, attr_name) < c.stop.
>
> Ideally I could create a kind of concurrent iterator, that steps  
> through children's blocks of time and the object's event list in  
> tandem, rather than going over the whole events list once for every  
> child produced. Maybe that's not possible... I'm pasting the whole  
> function down below, just for the hell of it.
>
> Thanks again,
> Eric
>
>
> def _iter_children(self, child, require_events=False):
>        """
>        Iterate through an object's 'child' items.
>
>        If require_events == True, only return children with
>        events, otherwise return all children.
>        """
>        while self.sentinel < self.stop:
>            c = child([], self.sentinel, self.start_attr,  
> rolling=self.rolling, counts_only=self.counts_only)
>            for e in self.events:
>                if c.start <= getattr(e,self.start_attr) < c.stop:
>                    c.events.append(e)
>            self.sentinel += c.dt_range
>            if not require_events or c.has_events():
>                yield c
> _______________________________________________
> Tutor maillist  -  Tutor at python.org
> http://mail.python.org/mailman/listinfo/tutor



More information about the Tutor mailing list