Python/Tutorial/Basic Types and Operators/Boolean Operators

From Jonathan Gardner's Tech Wiki
Jump to: navigation, search

not

The 'not' operator negates the value it works on. What was True evaluates to False, while False evaluates to True.

>>> not True
False
>>> not False
True

Remember that all values in Python have a truth.

>>> not 1
False
>>> not 0
True
>>> not 100/10 - 10
True

and

And evaluates to True only if both sides are True. Otherwise, it is False.

>>> True and Frue
True
>>> True and False
False
>>> False and True
False
>>> False and False
False

Thanks to the magic of short-circuiting, if you pass in values other than bools, it actually follows the following rules:

  1. First, evaluate the left side.
  2. If the left side is False, then return the left side and stop.
  3. If the left side is True, then evaluate the right side and return that.

This is so that you can do some magical things:

>>> 1 and 2
2
>>> 1 and 0
0
>>> 0 and 5
0

Note that the fact that the right hand side doesn't get evaluated if the left hand side is True is really, really, important! I often use 'and' (and its partner 'or' below) just to take advantage of this very useful property!

# Check to see if there is someone home. If there is, then knock on the door, and return that. Otherwise, return False.
is_somebody_home() and knock()

You can "chain" these together indefinitely, which will evaluate each expression, stopping at the first one that evaluates to false.

# as long as knock() returns True, keep knocking until you've knocked 4 times.
knock() and knock() and knock() and knock()

Of course, you don't want to go overboard here. We'll cover the if statement shortly, which will allow you to be far more creative and specific about conditional logic.

In Practice

I frequently use and to prevent execution of some expression if the expression is missing key elements. For instance:

foo and int(foo)

If 'foo' is False (empty, None, or 0), then it will simply return that. If it is true, then it will call the int function with it as a parameter, and return that instead.

or

or behaves much like and except it is true if either side is true.

>>> True or True
True
>>> True or False
True
>>> False or True
True
>>> False or False
False

Like the and operator, it really does this:

  1. Evaluate the left side.
  2. If the left side is True, then you're done. Return that value.
  3. If the left side is False, then evaluate the right side and return that.

Like and above, you can chain ors, which will keep evaluating the expression until something is False, stopping there.

In Practice

I frequently use or to conditionally assign a value. For instance:

x = y or 5

What the above does is assigns y to x, unless y is False. If y is False, then it will assign 5.

x if y else z

This special, trinary operator has three parameters. What it does is the following.

  1. Evaluate y.
  2. If y is True, then evaluate x and return that.
  3. If y is False, then evaluate z and return that instead.

In Practice

This is great for simple things like:

"You have "+n_mangoes+(" mango" if n_mangoes == 1 else " mangoes")

If n_mangoes is 1, then it evaluates to:

"You have "+n_mangoes+" mango"

Otherwise, it evaluates to:

"You have "+n_mangoes+" mangoes"

(NOTE: I had to use parentheses, otherwise Python would've interpreted the expression incorrectly.)

Precedence

The boolean or logical operators are the weakest binding operators (besides lambda). This means that Python does all other operations before evaluating the boolean operators. Example:

1 - 1 or 7 + 5

is interpreted as:

(1 - 1) or (7 - 5)

Among the logical operators, the tightest binding operators are, in order:

  • not
  • and
  • or
  • if-else

This means the following expression:

x or not y and b if c and d else a or b

Is interpreted as:

(x or ((not y) and b)) if (c and d) else (a or b)

This is, obviously, confusing, so I encourage you to use parentheses wherever possible.

The most common pitfall here is chaining or's and and's together. You won't get what you expect:

a and b or c and d or e and f

is:

(a and b) or (c and d) or (e and f)

If you think of 'not' as the negative '-' (not minus), and as '*' and or as '+', then you won't make many mistakes, because those operators have the same precedence as the others.

Simplifying Expressions

Identities

False and a => False
True  and a => a
False or a  => a
True  or a  => True

When and'ing and or'ing with values that never change, the result is simplified.

And'ing / Or'ing the Same Value

Oftentimes, you can simplify hairy logical expressions. Here are a few simplifications.

a or a   => a
a and a  => a

In both cases, if you are anding or oring the same value, then simply rewrite that as "a".

a or not a  => True
a and not a => False

Double Nots

Sometimes, you'll find something like the above. Anding with the inverse always gives False, while or'ing with the inverse always gives True.

not not a => a

Combining two not's together just inverses what was already inversed, bringing it back to the original truth value. NOTE, however, that the result of this expression is either True or False, and not what the original value was.

Association

You can reverse many logical expressions, provided you are only interested in the truth.

a and b <=> b and a
a or b  <=> b or a

However, if you're using the short-circuiting feature, this will give you the wrong results.

Distribution

a and (b or c) <=> a and b or a and c
a or b and c <=> (a or b) and (a or c)

Remember precedence! 'and' before 'not'.

I rarely encounter this, but when I do, I appreciate the simplification.

DeMorgan's Laws

One mathematical law I'd like you to become familiar with is DeMorgan's Laws. They simply state:

not a and not b <=> not (a or b)
not a or not b  <=> not (a and b)

If you find yourself using an expression like the above, consider rewriting it using DeMorgan's Law and you may be pleasantly surprised by how simple it reads.

For instance:

a != 7 and a != 6

can be rewritten as:

not (a==7 or a==6)

Truth Tables

Oftentimes, I'll confuse the heck out of myself when I'm writing logical expressions using the above.

One way I can help myself figure things out is to draw up a truth table. What I do is I create a table. The first columns I use are the possible inputs. Then I put each intermediate expression as a column after that, with the last column being the final evaluation of the expression.

I then create rows for each possible combination of inputs. When I am satisfied I have them all, I stop adding rows.

For each row, what I do is carefully calculate the intermediate values and the final value given the inputs listed. When I am done, I have a truth table.

In Reverse

In other cases, I want to build an expression, but I don't know what the expression should be. I do know, however, what the result should be given the inputs.

Just like before, I draw up a table, except I have no intermediate values. So I list the inputs and the expected output. Then I create rows for each possible input, and then I fill in what the output should be for that combination.

This last step is tricky, but if you've seen enough truth tables, you can figure it all out in mere moments. I stare at the table, and try to find patterns. These are the questions I ask.

  1. Is the result always connected to one particular value?
  2. Is the result connected to the inverse of one value? This suggest a 'not' somewhere.
  3. If it only works for some rows, then at this point I have to see which other inputs turn that "on" or "off. This suggests "and" and "or".
  4. As I work it out, I start building out, at least in my mind, intermediate values for potential sub-expressions. Some of them I will throw away because they don't work, but at least I'll know what doesn't work.

As you plug away, trying out different expressions, you are sure to eventually hit the right combination.

As a general rule, you should not have more 'and' and 'or' operators than you have inputs.