Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing for an empty iterator in a Python for... loop

The code below is based on this recipe. However, the key point of the recipe - that it provides a way to break out of the iteration on an iterator if the iterator is empty - doesn't seem to work here, instead behaving in the following undesired ways:

  1. If get_yes_no_answer() == False and there are two or more items left in the iterator, next_choice is skipped, rather than being selected on the next iteration.
  2. If get_yes_no_answer() == False and there are less than two items left in the iterator, my_func() returns None.

How can I ensure that:

  • If get_yes_no_answer() == False and there are two or more items left in the iterator, next_choice is not skipped?
  • If get_yes_no_answer() == False and there is one item left in the iterator, my_func() prints it and calls get_yes_no_answer()?
  • If get_yes_no_answer() == False and there are no items left in the iterator, the except StopIteration clause is triggered?

Here's the code:

def my_func(choice_pattern, input):
# Search in input for some things to choose from.
choice_iterator = choice_pattern.finditer(input, re.M)
if not choice_iterator:
    print "No choices. Exiting..."
    sys.exit()
else:
    # Show choices to the user. For each one, ask user for a yes/no response. If
    # choice accepted, return a result. Otherwise show user next choice. If no
    # choices accepted by user, quit.
    for choice in choice_iterator:
        print choice.group()
        # get_yes_no_answer() returns True or False depending on user response.
        if get_yes_no_answer():
            return choice
        else:
            # Check if iterator is empty. If so, quit; if not, do something else.
            try:
                next_choice = choice_iterator.next()
            except StopIteration:
                print "No matches. Exiting..."
                sys.exit()
            else:
                choice_iterator = itertools.chain([next_choice], choice_iterator)

2 Answers

why are you doing it this way at all? why not just:

def get_choice(pattern, inpt):
    choices = pattern.finditer(inpt, re.M)
    if not choices:
        sys.exit('No choices')
    for choice in choices:
        print(choice.group(0))
        if get_yes_no_answer():
            return choice
    sys.exit('No matches')

I don't know what your is the length of your input but I doubt it's worth the trouble.

like image 90
SilentGhost Avatar answered Oct 26 '25 16:10

SilentGhost


You don't need to check if the iterator is empty. The for loop will do that for you, and stop when the iterator is empty. It's as simple as that.

Also, you don't need the else after the sys.exit() or the return.

That done, your code looks like this:

def my_func(choice_pattern, input):
    # Search in input for some things to choose from.
    choice_iterator = choice_pattern.finditer(input, re.M)
    if not choice_iterator:
        print "No choices. Exiting..."
        sys.exit()

    # Show choices to the user. For each one, ask user for a yes/no response. If
    # choice accepted, return a result. Otherwise show user next choice. If no
    # choices accepted by user, quit.
    for choice in choice_iterator:
        print choice
        # get_yes_no_answer() returns True or False depending on user response.
        if get_yes_no_answer():
            return choice
    # Loop exited without matches.
    print "No matches. Exiting..."
    sys.exit()

That's it!

What happens is that you in the loop, also gets the next item. The result is that you in fact only show every second answer.

In fact, you can simplify it even more:

def my_func(choice_pattern, input):
    choice_iterator = choice_pattern.finditer(input, re.M)
    if choice_iterator:
        for choice in choice_iterator:
            print choice
            if get_yes_no_answer():
                return choice
    # If there is no choices or no matches, you end up here:
    print "No matches. Exiting..."
    sys.exit()

Iterators are used pretty much as any sequence type. You don't need to treat it differently from a list.

like image 30
Lennart Regebro Avatar answered Oct 26 '25 17:10

Lennart Regebro



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!