Expression can be simplified on list

Terry Reedy tjreedy at udel.edu
Thu Sep 29 14:10:04 EDT 2016


On 9/29/2016 12:36 PM, MRAB wrote:
> On 2016-09-29 16:56, Steve D'Aprano wrote:
>> On Thu, 29 Sep 2016 09:53 pm, MRAB wrote:
>>
>>> What if an _exhausted_ iterator was falsey?

Logic is about binary distinctions, rather than about 'truth'.  For 
non-buggy iterator it, the useful binary distinction is whether next(it) 
returns an object, the truthy action, or raises StopIteration, the 
falsey action.  The for loop machinery converts this binary action 
distinction into another binary action distinction: execute the suite 
and call next again versus proceed to the else clause if there is one or 
do nothing.  In other words, for loops indeed treat an exhausted 
iterator as falsey.

>> The problem is that in general you can't tell if an iterator is exhausted
>> until you attempt to advance it. So even if bool(iterator) returns True,
>> the call to next() may raise StopIteration:
>>
> [snip]
>
> By "exhausted" I meant "has previously raised StopIteration".

The iterator protocol could have required that iterators have 
'self._bval = True' in .__init__, method 'def __bool__(self): return 
self._bval, and 'self._bval = False' in .__next__ just prior to 'raise 
StopIteration'.

However, the iterator protocol is intentionally as simple as possible 
and the extra baggage would nearly always be useless.  If you use a 
non-iterator iterable in a for statement, the temporary iterator is 
discarded as soon as it raises StopIteration.  If you pass an iterator 
to a function that iterates, you likely created it on the fly as an 
expression, and it will be discarded when the function returns.  If you 
do have a reference, you can (and perhaps should) delete it after the 
function returns.

Still, if one has a specialized use-case for iterables with the extra 
baggage, one can create them as instances of the following wrapper.

class BoolIter:
     def __init__(self, iterable):
         self._it = iter(iterable)
         self._bval = True
     def __iter__(self):
         return self
     def __bool__(self):
         return self._bval
     def __next__(self):
         try:
             return next(self._it)
         except StopIteration:
             self._bval = False
             raise

--
Terry Jan Reedy




More information about the Python-list mailing list