Search code examples
pythonpython-3.xlistbooleanany

How to turn an any() statment negative in Python?


I'm creating a check for input e-mails to see if they only use specific charakters. I wanted to use an any() statment that basically means "if any item from the e-mail isn't in goodCharsEmail, do ..." I came up with this:

newEmail = ["m", "a", "i", "l", "@", "d", "o", "m", "a", "i", "n", ".", "c", "o", "m"] 

goodCharsEmail = ["a", "b", "c", "etc..."]

check =  any(item in newEmail for item in goodCharsEmail)
if check == True:
    print("bad request")
    quit

But I obviously need to make the any() statment negative, because at the moment it does the exact opposite of the wanted result. How can I fix this?


Solution

  • Since you mentioned you're new to python, I will try to be very thorough. Please excuse me if some of this is repetition.

    First, let's look at what any() does. The simplest way is probably to write our own simple implementation:

    def any(sequence):
        for value in sequence:
            if value:
                return True
        return False
    

    In English: it returns True if-and-only-if any value in the sequence is "true-ish" (e.g. a non-zero integer, a non-empty list, a non-empty string, and so on).

    Since all() was mentioned in the comments, let's do the same for that one:

    def all(sequence):
        for value in sequence:
            if not value:
                return False
        return True
    

    In English: it returns True if-and-only-if all values in the sequence are true-ish. Very similar to any(), isn't it?

    Second, let me note a thing about python strings: they can often be thought of as lists of characters, and handled as such. In other words, there is no need to separate the characters into a list of separate one-character strings.

    Third, I believe your problem is not with the any() function, but with your list comprehension (the expression inside the parentheses). You see, item in newEmail for item in goodCharsEmail means that you're checking whether every character in goodCharsEmail is present in newEmail. What you really want is the other way around: is every character in newEmail one of the good characters?

    You could write this with all:

    not all(c in goodCharsEmail for c in newEmail)
    

    ...or any:

    any(c not in goodCharsEmail for c in newEmail)
    

    These two are equivalent -- pick the one you think is easier to read and understand.

    The key to understanding these comprehension expressions may be to understand how the parts interact. c in goodCharsEmail for c in newEmail means evaluate the expression "c in goodCharsEmail" for every c in newEmail.

    So, here's how I would rewrite the validation code you posted to make it work:

    newEmail = "[email protected]" 
    goodCharsEmail = "@.abcdefghijkl..." # TODO: more chars, probably
    if any(c not in goodCharsEmail for c in newEmail):
        print("bad request")
        quit
    

    Finally: email address validation is, unfortunately, much harder than most of us would think. As mentioned in this blog post, all of the following (and more!) are valid email addresses:

    Your approach is fine if you're just practicing in a new programming language. If you're coding the validation for some app or website that other people are going to use, it may cause some frustration.

    Have fun! :)