Search code examples
python-3.xstringdictionarynonetype

why my code returns TypeError: 'NoneType' object is not iterable?


i am trying to define a function which checks strings whether they contains the word from a dictionary and return true along with which word matched . Below is the snippet of code , everything works fine when a word matches in the string from dictionary.

def trigcheck(strings,a):
    try:
        str = strings.split()
        m=''
        a_match = [True for match in a if match in str]
        a=[m for m in a if m in str]
        if True in a_match:
            return True,a[0]

    except:
        return False,""

bool1,w=trigcheck("kjfdsnfbdskjhfbskdabs",['hello','do'])
print(bool1)
print(w)

I was expecting that with string that doesn't match should return False and ' ' . But it throws error saying:

bool1,w=trigcheck("kjfd s n f dobdskjhfbskdabs",['hello','do'])
TypeError: 'NoneType' object is not iterable

Solution

  • If you don't raise an exception, and True is not in a_match, you don't explicitly return at all, causing you to return None implicitly. Unpacking None to bool1 and w is what raises the exception.

    Fix your code by making the exceptional return unconditional if the if check fails:

    def trigcheck(strings,a):
        try:
            str = strings.split()
            m=''
            a_match = [True for match in a if match in str]
            a=[m for m in a if m in str]
            if True in a_match:
                return True,a[0]
    
        except Exception:  # Don't use bare except unless you like ignoring Ctrl-C and the like
            pass
        # Failure return is outside except block, so fallthrough reaches
        # it whether due to an exception or because the if check failed
        return False,""
    

    Additional side-note: Your test for a match existing is relatively inefficient; it can't short-circuit, and requires a temporary list. Replacing the body of your function with the following code which relies on your exception handling to return when there are no matches:

    def trigcheck(strings,a):
        try:
            strings = strings.split()  # Don't nameshadow builtins, reuse strings instead of shadowing str
            return True, next(m for m in a if m in strings)
        except StopIteration:
            return False, ""
    

    reduces three scans and a two temporary lists to a single scan and no temporary lists, and avoids silencing random exceptions (e.g. a TypeError because someone passed a non-string or non-iterable as an argument) by only catching the one that indicates a failure to find a match.