Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

`for` loop using generators, return different value if exhausted

I am looping over an itertools.permutation object and for efficiency, the loop breaks once the item is found. I understand that because I used the for loop I am unable to catch the StopIteration error when the generator is exhausted and the item is not found.

So far I have implemented a found flag, but it seems kinda hacky.

from itertools import permutations

def verify_string(name, compare):
    name = name.lower().split()
    compare = compare.lower().split()
    p = permutations(compare, len(compare))
    found = 0

    for i in p:
        if list(i) == name:
            print(f'Found: {compare} in {name}')
            found = 1
            break

    if not found:
        print('Not Found')

name = 'guido van rossum'
compare = 'van guido rossum'

verify_string(name, compare)
>>Found: ['van', 'guido', 'rossum'] in ['guido', 'van', 'rossum']

I also thought of checking if not next(p, '') to see if it is exhausted but the item might be found in the generator's last item and will return True anyways.

From a Pythonic view, is there a way to manage looping over a generator that stops and return when an item is found and returns a different value only when the generator is exhausted.

like image 824
BernardL Avatar asked Dec 17 '25 08:12

BernardL


2 Answers

The Pythonic way is to use a for-else loop.

from itertools import permutations

def verify_string(name, compare):
    name = name.lower().split()
    compare = compare.lower().split()    
    for i in permutations(compare, len(compare)):
        if list(i) == name:
            print(f'Found: {compare} in {name}')
            break
    else:  # Raymond Hettinger calls this "if no break" condition
        # If we did not break out of the "for loop", execute this.
        print('Not Found')

name = 'guido van rossum'
compare = 'van guido rossum'

verify_string(name, compare)
>>> Found: ['van', 'guido', 'rossum'] in ['guido', 'van', 'rossum']

Edit

My initial reply was to how to avoid using the found flag and I wasn't paying attention to what you were actually trying to do. The for-else construct is also a very useful and often neglected language construct that I wanted to highlight.

However, if you just want to check if the set of string is a permutation of another, then why not just

match = sorted(name.lower().split()) == sorted(compare.lower().split())

This avoids the need to go through all possible permutation of the words in the string.

like image 62
lightalchemist Avatar answered Dec 19 '25 21:12

lightalchemist


From a Pythonic view, is there a way to manage looping over a generator that stops and return when an item is found and returns a different value only when the generator is exhausted.

As you don't actually return anything - if you fix that part of your function, you'll return None if there are no matches:

from itertools import permutations

def verify_string(name, compare):
    name = name.lower().split()
    compare = compare.lower().split()    
    for i in permutations(compare, len(compare)):
        if list(i) == name:
            return True

name = 'guido van rossum'
compare = 'van guido rossum'

if verify_string(name, compare):
  print(f'Found: {compare} in {name}')
else:
  print('Not found')
like image 20
Burhan Khalid Avatar answered Dec 19 '25 22:12

Burhan Khalid



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!