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 cannot use len (iterable_sequence), because 1) not all sequences have known lengths; 2) in some cases, the len () call can initiate the loading of sequence elements into memory (as is the case with the sql query results).

I ask that I am just wondering if there is a way to make it more concise and idiomatic. What I'm looking for is as follows:

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

(assuming "else" here only works on empty sequences that I know of, it isnโ€™t)

+8
python syntax for-loop
source share
5 answers
 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 present.


An example implementation of empty_adapter() 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 can use it as follows:

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

Presenting a special case for an empty sequence for a general case seems wrong. There must be a better solution to the problem with the domain.

+7
source share

It may be work for itertools.tee. You "run" the sequence when checking, but after that you remain an untouched copy of the sequence:

 from itertools import tee check, sequence = tee(sequence, 2) try: check.next(): except StopIteration: #empty sequence for item in sequence: #do stuff 

(it is worth noting that tee does the โ€œrightโ€ thing here: it only loads the first element of the sequence at the time check.next() is executed - and this first elument will remain available in the sequence The rest of the elements will be extracted only as part of the for loop Or simply: you cannot use len, you cannot check if the sequence is bool True, for the same reasons.

Therefore, your path is quite simple - another way would be to remove the name "item" before the "for" statement and check if it exists after the loop:

 del item for item in sequence: # do stuff try: item except NameError: # sequence is empty. 

But your code should be used as more comprehensible than this.

+3
source share

The second example from JF Sebastian seems to be a ticket with a while loop.

 NoItem = object() myiter = (x for x in range(10)) item = next(myiter, NoItem) if item is NoItem: ... else: while item is not NoItem: print item item = next(myiter, NoItem) 

Not the shortest, but objectively the clearest ... Dirt, no?

+2
source share

This should not call 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, # ie 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)) 
+1
source share
 if not iterable_sequence.__length_hint__(): empty() else: for item in iterable_sequence: dostuff() 
0
source share

Source: https://habr.com/ru/post/650533/


All Articles