Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

better for-loop syntax for detecting empty sequences?

Is there a better way to write the following:

   row_counter = 0
   for item in iterable_sequence:
      # do stuff with the item

      counter += 1

   if not row_counter:
      # handle the empty-sequence-case

Please keep in mind that I can't use len(iterable_sequence) because 1) not all sequences have known lengths; 2) in some cases calling len() may trigger loading of the sequence's items into memory (as the case would be with sql query results).

The reason I ask is that I'm simply curious if there is a way to make above more concise and idiomatic. What I'm looking for is along the lines of:

for item in sequence:
   #process item
*else*:
   #handle the empty sequence case

(assuming "else" here worked only on empty sequences, which I know it doesn't)

like image 514
Dmitry B. Avatar asked Dec 29 '10 19:12

Dmitry B.


2 Answers

This shouldn't trigger len():

def handle_items(items):
    index = -1
    for index, item in enumerate(items):
        print 'processing item #%d: %r' % (index, item)
    # at this point, index will be the offset of the last item,
    # i.e. length of items minus one
    if index == -1:
        print 'there were no items to process'
    print 'done'
    print

# test with an empty generator and two generators of different length:
handle_items(x for x in ())
handle_items(x for x in (1,))
handle_items(x for x in (1, 2, 3))
like image 167
akaihola Avatar answered Sep 21 '22 11:09

akaihola


for item in iterable:
    break
else:
    # handle the empty-sequence-case here

Or

item = next(iterator, sentinel)
if item is sentinel:
   # handle the empty-sequence-case here   

In each case one item is consumed if it is present.


An example of empty_adapter()'s implementation mentioned in the comments:

def empty_adaptor(iterable, sentinel=object()):
    it = iter(iterable)
    item = next(it, sentinel)
    if item is sentinel:
       return None # empty
    else:
       def gen():
           yield item
           for i in it:
               yield i
       return gen()

You could use it as follows:

it = empty_adaptor(some_iter)
if it is not None: 
   for i in it:
       # handle items
else:
   # handle empty case

Introducing special case for an empty sequence for a general case seems wrong. There should be a better solution for a domain specific problem.

like image 36
jfs Avatar answered Sep 21 '22 11:09

jfs