0

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?

1337HaxX0r
  • 53
  • 6
  • 5
    I think you're looking for `not all()` – alkasm Aug 29 '19 at 21:13
  • 3
    Or `any(item not in...)` – Daniel Roseman Aug 29 '19 at 21:15
  • 3
    As an aside, it would be better to make `newEmail` a set. – Daniel Roseman Aug 29 '19 at 21:16
  • I'm confused about `goodCharsEmail`. Are you saying that any character in `newEmail` must be a character in one of the elements in `goodCharsEmail`? Is `goodCharsEmail` a list of allowable characters, or a list of allowable email addresses? – wcarhart Aug 29 '19 at 21:37
  • a list of acceptible charakters – 1337HaxX0r Aug 29 '19 at 21:43
  • @Boris Thank you, I'm pretty new to python. I don't really understand how alkasm's "not all()" should work and Daniel Roseman's "any(item not in...)" doesn't work for me either ---> "TypeError: 'bool' object is not iterable". – 1337HaxX0r Aug 30 '19 at 19:57
  • 1
    @1337HaxX0r I think those people missed that you're iterating over each character in `good_chars_email` and checking if they're in the email, when you should be iterating over the characters in the email and checking if they're in `good_chars_email` instead. Like this: `check = not all(c in good_chars_email for c in new_email)` or `check = any(c not in good_chars_email for c in new_email)` – Boris Verkhovskiy Aug 30 '19 at 20:15
  • Also, strings already behave like lists of characters, so you can just do `new_email = "mail@domain.com"` and `good_chars_email = "abc... etc"` and all your code will work exactly the same. – Boris Verkhovskiy Aug 30 '19 at 20:16
  • Finally, if you're trying to check that an email address is a valid email address, you should just use the `email.utils.parseaddr` function. Check out this question https://stackoverflow.com/a/14485817/3064538 – Boris Verkhovskiy Aug 30 '19 at 20:17
  • @Boris it works perfectly! Thank you so much! For me you've been the only user on here actually being helpful and cooperative to new contributors like me. :) – 1337HaxX0r Aug 30 '19 at 20:19

1 Answers1

1

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 = "mail@domain.com" 
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:

  • "Fred Bloggs"@example.com
  • $A12345@example.com
  • !def!xyz%abc@example.com

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! :)

Snild Dolkow
  • 6,669
  • 3
  • 20
  • 32
  • Thank you both for explaining how it needs to be done when you do it somehow my way. I also found this https://pypi.org/project/validate_email/ , what makes it easier and also checks if the email actually exists. – 1337HaxX0r Aug 30 '19 at 20:37