Why ever would one want to use the Not operator?

I’m just wondering why anyone would choose to use a Not operator when as far as I can tell the normal operators can do the same thing.

Why not? It’s simple:

if (!boolvar) {
    //Do stuff if boolvar is false
}

is easier than

if (boolvar) {} else {
    //Do stuff if boolvar is false 
}
1 Like

I suppose in an actual project it’ll seem like a much bigger deal than a few characters saved.

Let’s consider, for a moment, an example in Python.

Python is a dynamically typed language, which means that the interpreter doesn’t know what type a variable is until it’s assigned a value. This is why we simply write myVariable = "cheese". Only at the point that the interpreter evaluates that line does Python know that myVariable is a string.

Now, let’s imagine we have written a function which expects to be passed two arguments - one which is a string, and another which is an integer.

def wordsWordsWords(word, repeat):
    return word * repeat

Our function works fine if we provide arguments that are of acceptable types; for example, I can do this:

>>> wordsWordsWords("Leeds",3)
'LeedsLeedsLeeds'

or this

>>> wordsWordsWords(["We","Will","We","Will","Rock","You"],2)
['We', 'Will', 'We', 'Will', 'Rock', 'You', 'We', 'Will', 'We', 'Will', 'Rock', 'You']

but I can’t do this:

>>> wordsWordsWords("Cool",24.2)
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    wordsWordsWords("Cool",24.2)
  File "spam.py", line 2, in wordsWordsWords
    return word * repeat
TypeError: can't multiply sequence by non-int of type 'float'

We’ve happily got away with passing unintended data types in to our function until we tried to use a float. If we wanted to restrict our function to only processing the intended string and integer, we can change it as follows:

def wordsWordsWords(word, repeat):
    if not isinstance(word, str) or not isinstance(repeat, int):
        print("Unexpected types! 'word' (%r) must be str, 'repeat' (%r) must be int!" % (word,repeat))
        return None
    return word * repeat

Now, if I try and provide an unexpected data type, I get a message telling me I’ve got it wrong instead of my program crashing.

>>> wordsWordsWords("Cool",24.2)
Unexpected types! 'word' (Cool) must be str, 'repeat' (24.2) must be int!

Note that, of course, we could do the opposite here. We could just as easily write:

def wordsWordsWords(word, repeat):
    if isinstance(word, str) and isinstance(repeat, int):
        return word * repeat
    else:
        print("Unexpected types! 'word' (%r) must be str, 'repeat' (%r) must be int!" % (word,repeat))
        return None

However, in this instance we are working with a relatively trivial example.

If we were working on a larger function, by testing for the affirmative (i.e. if isinstance(word,str) and isinstance(repeat,int)) before proceeding the remainder of our function program is already indented by 8 spaces. If our function itself uses some conditionals or loops etc, we could very well end up with code which has been indented by 12, 16 or more spaces…

Excessively indented code can hamper readability - not to mention that PEP-8 suggests limiting lines of Python code to 72 characters.

By using not, we can quickly determine that the provided arguments are not correct and exit the function gracefully. For arguments of the correct type, the body of our function code is indented only the once and we’ve got more room to write readable Python.

Hope that helps a bit. :slight_smile: