# More on if

## `elif` clauses

We've already seen that an `if` statement can optionally have an `else` clause. It can also optionally have any number of `elif` clauses — an abbreviation for “else if” — preceding the `else` clause.

A classic application where `elif` is appropriate is in translating a score into a letter grade.

```if score >= 90:     grade = 'A' elif score >= 80:     grade = 'B' elif score >= 70:     grade = 'C' elif score >= 60:     grade = 'D' else:     grade = 'F' ```

The way this works is as follows: Upon encountering the `if` statement, the computer first checks the `if` statement's condition, and if it passes, it does the body of the `if` clause (“`grade = 'A'`”) and it is done with the whole statement. But if that condition fails, it proceeds to the next `elif` clause's condition; if that succeeds, it does that `elif` clause's condution and then is done with the whole statement. But if it fails too, it proceeds to the next `elif` clause's condition, going down until it finds the first condition that is met; or, if they all fail, doing the `else` clause's body (if it exists).

In this case, if score is 75, the first condition “`score >= 90`” fails, and then the second condition “`score >= 80`” fails, but the third condition “`score >= 70`” succeeds. So the body “`grade = 'C'`” is executed and we are done with the statement. While it happens to be true that the fourth condition “`score >= 60`” would also be successful, it is never even noticed, because the computer stops upon the first successful condition it finds.

Notice how the e's in `elif` and `else` all line up under the i in `if`. This is necessary for Python to recognize that they are all part of the same statement.

## Boolean type

Just as “`x + 4`” is an expression — that is, some Python code that is evaluated to be a value — so is “`x < 4`”. In the case of “`x < 4`”, the resulting value is not a number but a value that is either true (if `x` is below 4) or false. Such a true/false value is called the Boolean type.

The Boolean type is most often used implicitly in programs, so that you don't even think about it. But it does occassionally show up explicitly. The following program — to see if all words in a sentence start with the letter p — is an example. Notice how it references `True` and `False` — Python's way of writing a true/false value directly into a program. (Oddly, `True` and `False` have to be capitalized.)

```words = input().split() all_ps = True for w in words:     if w[0] != 'p':         all_ps = False if all_ps:     print('All words start with p') else:     print("Some words don't start with p") ```

To see this work, suppose the user types “peter piper picked a peck”.

 `w`: peter piper picked a peck `all_ps`: `True` `False`

It then reaches the final `if` statement. Notice that the `if` statement has simply “`all_ps`” as its condition. This is entirely legitimate, because the `if` is designed to take true/false values, and that is exactly what `all_ps` is. When the condition is true, we execute the `if`'s body; and when it is false, we execute the `else`'s body. In our example, `all_ps` ends up `False`, so the clause's body executes, displaying “Some words don't start with p”.

By the way, this program works very differently if we change the inner `if` to:

```if w[0] != 'p':     all_ps = False else:     all_ps = True ```

In this case, the final word (peck) would end up leading the `else` clause to be executed, which would set `all_ps` back to `True`. So the program would incorrectly display “All words start with p”.

## Logic operators: `and`, `or`, `not`

Quite often we want to test multiple conditions on a line. We can combine two conditions together using the `and` operator. For example, suppose for some reason we want to test whether a variable `num` is both even and at least 100.

```if num % 2 == 0 and num >= 100:     print('Number is large and even') ```

Technically, `and` is an operator, like `+` or `==`. In this case, though, `and` is built to take two Boolean values and produce a Boolean value in response. If both are true, the result is true.

We can also use the word `or` as an operator.

```if num % 2 == 0 or num >= 100:     print('Number is large or even') ```

In this case, we'll enter the `if` if `num` is even or if `num` is at least 100 — or if they both are. So 24, 167, and 102 would all lead to “Number is large or even” being displayed.

Like `and`, `or` is designed to deal with two Boolean values. To our English-speaking brains, the following makes perfect sense:

```if num == 2 or 4:     print('Number is 2 or 4') ```

But Python interprets this as “`(num == 2) or (4)`” — that is, `or` is an operator whose precedence level is lower than `==`, just as has a lower precedence than `*`. The second part — 4 — is true no matter what, so the value of the overall `or` is true regardless of `num`. So this little fragment always displays “Number is 2 or 4”, even if `num` is something completely different, like 37.

So how should we write this? We need to make sure “`num ==`” appears after `or` as well.

```if num == 2 or num == 4:     print('Number is 2 or 4') ```

Going to back to our even/large example, what if we somehow want to test whether a number is even or large, but not both? We can do this using the `not` operator, along with `and` and `or`.

```if (num % 2 == 0 or num >= 100) and not (num % 2 == 0 and num >= 100):     print('Number is either large or even, but not both') ```

You'd want the parentheses here. Actually, Python does have an order of operations among the Boolean operators: `not` comes before `and`, which comes before `or`. But many experienced programmers prefer to add parentheses to make the order easier to understand. (In this case, the parentheses are actually required to get the order we want.)

## Short-circuit logic operators

With `and`, Python will evaluate the first part first. If it turns out to be false, then there is no point in evaluating the second part, and so Python won't even bother. This is called short-circuit behavior.

Short-circuiting is often useful. For instance, suppose we want to test whether the third letter of a word is the letter z. Naturally, you might be tempted to write “`if word[2] == 'z':`” (remembering that slot 2 holds the third letter, since the slots are numbered starting from 0). However, when the word has only two letters, `word[2]` doesn't exist, and so Python would simply crash the program. The solution is to test whether the word has at least three letters before examining the third letter.

`if len(word) >= 3 and word[2] == 'z':`

If the word has only two letters, then the first condition will be false and so Python won't even bother with the second part — which is a good thing, because if it did, the program would crash. However, if the word does have three letters or more, it would go on to check the third letter.