Good use for itertools.dropwhile and itertools.takewhile

MRAB python at mrabarnett.plus.com
Wed Dec 5 12:57:19 EST 2012


On 2012-12-05 13:45, Chris Angelico wrote:
> On Wed, Dec 5, 2012 at 12:17 PM, Nick Mellor <thebalancepro at gmail.com> wrote:
>>
>> takewhile mines for gold at the start of a sequence, dropwhile drops the dross at the start of a sequence.
>
> When you're using both over the same sequence and with the same
> condition, it seems odd that you need to iterate over it twice.
> Perhaps a partitioning iterator would be cleaner - something like
> this:
>
> def partitionwhile(predicate, iterable):
>      iterable = iter(iterable)
>      while True:
>          val = next(iterable)
>          if not predicate(val): break
>          yield val
>      raise StopIteration # Signal the end of Phase 1
>      for val in iterable: yield val # or just "yield from iterable", I think
>
> Only the cold hard boot of reality just stomped out the spark of an
> idea. Once StopIteration has been raised, that's it, there's no
> "resuming" the iterator. Is there a way around that? Is there a clean
> way to say "Done for now, but next time you ask, there'll be more"?
>
Perhaps you could have some kind of partitioner object:

class Partitioner:
     _SENTINEL = object()

     def __init__(self, iterable):
         self._iterable = iter(iterable)
         self._unused_item = self._SENTINEL

     def takewhile(self, condition):
         if self._unused_item is not self._SENTINEL:
             if not condition(self._unused_item):
                 raise StopIteration

             yield self._unused_item
             self._unused_item = self._SENTINEL

         for item in self._iterable:
             if not condition(item):
                 self._unused_item = item
                 break

             yield item

         raise StopIteration

     def remainder(self):
         if self._unused_item is not self._SENTINEL:
             yield self._unused_item
             self._unused_item = self._SENTINEL

         for item in self._iterable:
             yield item

         raise StopIteration

def is_all_caps(word):
     return word == word.upper()

part = Partitioner("CAPSICUM RED fresh from QLD".split())
product = " ".join(part.takewhile(is_all_caps))
description = " ".join(part.remainder())
print([product, description])




More information about the Python-list mailing list