- Notifications
You must be signed in to change notification settings - Fork 54
/
Copy pathConditionalStatements.md.txt
492 lines (373 loc) · 15.8 KB
/
ConditionalStatements.md.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
---
jupyter:
jupytext:
text_representation:
extension: .md
format_name: markdown
format_version: '1.3'
jupytext_version: 1.13.6
kernelspec:
display_name: Python 3
language: python
name: python3
---
<!-- #raw raw_mimetype="text/restructuredtext" -->
.. meta::
:description: Topic: Conditional Statements, Difficulty: Easy, Category: Section
:keywords: if, else, elif, inline if, switch statement, comparison operator, bool, truth, is operator
<!-- #endraw -->
# Conditional Statements
<div class="alert alert-warning">
**Note**:
There are reading-comprehension exercises included throughout the text. These are meant to help you put your reading to practice. Solutions for the exercises are included at the bottom of this page.
</div>
<!-- #region -->
In this section, we will be introduced to the `if`, `else`, and `elif` statements. These allow you to specify that blocks of code are to be executed only if specified conditions are found to be true, or perhaps alternative code if the condition is found to be false. For example, the following code will square `x` if it is a negative number, and will cube `x` if it is a positive number:
```python
# a simple if-else block
if x < 0:
x = x ** 2
else:
x = x ** 3
```
Please refer to the "Basic Python Object Types" subsection to recall the basics of the "boolean" type, which represents True and False values. We will extend that discussion by introducing comparison operations and membership-checking, and then expanding on the utility of the built-in `bool` type.
<!-- #endregion -->
<!-- #region -->
## Comparison Operations
Comparison statements will evaluate explicitly to either of the boolean-objects: `True` or `False`. There are eight comparison operations in Python:
| Operation | Meaning |
| --------- | ----------------------- |
| `<` | strictly less than |
| `<=` | less than or equal |
| `>` | strictly greater than |
| `>=` | greater than or equal |
| `==` | equal |
| `!=` | not equal |
| `is` | object identity |
| `is not` | negated object identity |
The first six of these operators are familiar from mathematics:
```python
>>> 2 < 3
True
```
Note that `=` and `==` have very different meanings. The former is the assignment operator, and the latter is the equality operator:
```python
>>> x = 3 # assign the value 3 to the variable `x`
>>> x == 3 # check if `x` and 3 have the same value
True
```
Python allows you to chain comparison operators to create "compound" comparisons:
```python
>>> 2 < 3 < 1 # performs (2 < 3) and (3 < 1)
False
```
Whereas `==` checks to see if two objects have the same value, the `is` operator checks to see if two objects are actually the *same* object. For example, creating two lists with the same contents produces two *distinct* lists, that have the same "value":
```python
# demonstrating `==` vs `is`
>>> x = [1, 2, 3]
>>> y = [1, 2, 3]
>>> x == y
True
# `x` and `y` reference equivalent, but distinct lists
>>> x is y
False
```
Thus the `is` operator is most commonly used to check if a variable references the `None` object, or either of the boolean objects:
```python
>>> x = None
>>> x is None
True
# (2 < 0) returns the object `False`
# thus this becomes: `False is False`
>>> (2 < 0) is False
True
```
Use `is not` to check if two objects are distinct:
```python
>>> 1 is not None
True
```
<!-- #endregion -->
<!-- #region -->
## `bool` and Truth Values of Non-Boolean Objects
Recall that the two boolean objects `True` and `False` formally belong to the `int` type in addition to `bool`, and are associated with the values `1` and `0`, respectively:
```python
>>> isinstance(True, int)
True
>>> int(True)
1
>>> isinstance(False, int)
True
>>> int(False)
0
>>> 3*True - False
3
>>> True / False
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-4-f8487d9d0863> in <module>()
----> 1 True / False
ZeroDivisionError: division by zero
```
Likewise Python ascribes boolean values to non-boolean objects. For example,the number 0 is associated with `False` and non-zero numbers are associated with `True`. The boolean values of built-in objects can be evaluated with the built-in Python command `bool`:
```python
# Using `bool` to access the True/False
# value of non-boolean objects
>>> bool(0)
False
```
and non-zero Python integers are associated with `True`:
```python
# nonzero values evaluate to `True`
>>> bool(2)
True
```
The following built-in Python objects evaluate to `False` via `bool`:
- `False`
- `None`
- Zero of any numeric type: `0`, `0.0`, `0j`
- Any empty sequence, such as an empty string or list: `''`, `tuple()`, `[]`, `numpy.array([])`
- Empty dictionaries and sets
Thus non-zero numbers and non-empty sequences/collections evaluate to `True` via `bool`.
<div class="alert alert-info">
**Takeaway**:
The `bool` function allows you to evaluate the boolean values ascribed to various non-boolean objects. For instance, `bool([])` returns `False` wherease `bool([1, 2])` returns `True`.
</div>
<!-- #endregion -->
<!-- #region -->
## `if`, `else`, and `elif`
We now introduce the simple, but powerful `if`, `else`, and `elif` conditional statements. This will allow us to create simple branches in our code. For instance, suppose you are writing code for a video game, and you want to update a character's status based on her/his number of health-points (an integer). The following code is representative of this:
```python
if num_health > 80:
status = "good"
elif num_health > 50:
status = "okay"
elif num_health > 0:
status = "danger"
else:
status = "dead"
```
Each `if`, `elif`, and `else` statement must end in a colon character, and the body of each of these statements is [delimited by whitespace](https://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Introduction.html#Python-Uses-Whitespace-to-Delimit-Scope).
The following pseudo-code demonstrates the general template for conditional statements:
```
if <expression_1>:
the code within this indented block is executed if..
- bool(<expression_1>) is True
elif <expression_2>:
the code within this indented block is executed if..
- bool(<expression_1>) was False
- bool(<expression_2>) is True
...
...
elif <expression_n>:
the code within this indented block is executed if..
- bool(<expression_1>) was False
- bool(<expression_2>) was False
...
...
- bool(<expression_n-1>) was False
- bool(<expression_n>) is True
else:
the code within this indented block is executed only if
all preceding expressions were False
```
In practice this can look like:
```python
x = [1, 2]
if 3 < len(x):
# bool(3 < 2) returns False, this code
# block is skipped
print("`x` has more than three items in it")
elif len(x) == 2:
# bool(len(x) == 2) returns True
# this code block is executed
print("`x` has two items in it")
elif len(x) == 1:
# this statement is never reached
print("`x` has one items in it")
else:
# this statement is never reached
print("`x` is an empty list")
"`x` has two items in it"
```
In its simplest form, a conditional statement requires only an `if` clause. `else` and `elif` clauses can only follow an `if` clause.
```python
# A conditional statement consisting of
# an "if"-clause, only.
x = -1
if x < 0:
x = x ** 2
# x is now 1
```
Similarly, conditional statements can have an `if` and an `else` without an `elif`:
```python
# A conditional statement consisting of
# an "if"-clause and an "else"
x = 4
if x > 2:
x = -2
else:
x = x + 1
# x is now -2
```
Conditional statements can also have an `if` and an `elif` without an `else`:
```python
# A conditional statement consisting of
# an "if"-clause and an "elif"
x = 'abc'
if len(x) < 9:
x = x * 3
elif len(x) > 40:
x = 'cba'
# x is now 'abcabcabc'
```
Note that only one code block within a single if-elif-else statement can be executed: either the "if-block" is executed, or an "elif-block" is executed, or the "else-block" is executed. Consecutive if-statements, however, are completely independent of one another, and thus their code blocks can be executed in sequence, if their respective conditional statements resolve to `True`.
```python
# consecutive if-statements are independent
x = 5
y = 0
if x < 10:
y += 1
if x < 20:
y += 1
# y is now 2
```
<!-- #endregion -->
<!-- #region -->
<div class="alert alert-info">
**Reading Comprehension: Conditional statements**
1\. Assume `my_list` is a list. Given the following code:
```python
first_item = None
if my_list:
first_item = my_list[0]
```
What will happen if `my_list` is `[]`? Will `IndexError` be raised? What will `first_item` be?
2\. Assume variable `my_file` is a string storing a filename, where a period denotes the end of the filename and the beginning of the file-type. Write code that extracts only the filename.
`my_file` will have at most one period in it. Accommodate cases where `my_file` does *not* include a file-type.
That is:
- `"code.py"` $\rightarrow$ `"code"`
- `"doc2.pdf"` $\rightarrow$ `"doc2"`
- `"hello_world"` $\rightarrow$ `"hello_world"`
</div>
<!-- #endregion -->
<!-- #region -->
### Inline if-else statements
Python supports a syntax for writing a restricted version of if-else statements in a single line. The following code:
```python
num = 2
if num >= 0:
sign = "positive"
else:
sign = "negative"
```
can be written in a single line as:
```python
sign = "positive" if num >=0 else "negative"
```
This is suggestive of the general underlying syntax for inline if-else statements:
<div class="alert alert-info">
**The inline if-else statement**:
The expression `A if <condition> else B` returns `A` if `bool(<condition>)` evaluates to `True`, otherwise this expression will return `B`.
</div>
This syntax is highly restricted compared to the full "if-elif-else" expressions - no "elif" statement is permitted by this inline syntax, nor are multi-line code blocks within the if/else clauses.
Inline if-else statements can be used anywhere, not just on the right side of an assignment statement, and can be quite convenient:
```python
# using inline if-else statements in different scenarios
>>> x = 2
# will store 1 if `x` is non-negative
# will store 0 if `x` is negative
>>> my_list = [1 if x >= 0 else 0]
>>> my_list
[1]
>>> "a" if x == 1 else "b"
'b'
```
We will see this syntax shine when we learn about comprehension statements. That being said, this syntax should be used judiciously. For example, inline if-else statements ought not be used in arithmetic expressions, for therein lies madness:
```python
# don't ever do this...ever!
2 - 3 if x < 1 else 1 + 6*2 if x >= 0 else 9
```
<!-- #endregion -->
<!-- #region -->
## Short-Circuiting Logical Expressions
Armed with our newfound understanding of conditional statements, we briefly return to our discussion of Python's logic expressions to discuss "short-circuiting". In Python, a logical expression is evaluated from left to right and will return its boolean value as soon as it is unambiguously determined, *leaving any remaining portions of the expression unevaluated*. That is, the expression may be *short-circuited*.
For example, consider the fact that an `and` operation will only return `True` if both of its arguments evaluate to `True`. Thus the expression `False and <anything>` is guaranteed to return `False`; furthermore, when executed, this expression will return `False` *without having evaluated* `bool(<anything>)`.
To demonstrate this behavior, consider the following example:
```python
# demonstrating short-circuited logic expressions
>>> False and 1/0 # evaluating `1/0` would raise an error
False
```
According to our discussion, the pattern `False and` short-circuits this expression without it ever evaluating `bool(1/0)`. Reversing the ordering of the arguments makes this clear.
```python
# expressions are evaluated from left to right
>>> 1/0 and False
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-1-3471672109ee> in <module>()
----> 1 1/0 and False
ZeroDivisionError: division by zero
```
In practice, short-circuiting can be leveraged in order to condense one's code. Suppose a section of our code is processing a variable `x`, which may be either a [number](https://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Basic_Objects.html#Number-Types) or a [string](https://www.pythonlikeyoumeanit.com/Module2_EssentialsOfPython/Basic_Objects.html#Strings). Suppose further that we want to process `x` in a special way if it is an all-uppercased string. The code
```python
# this will raise an error if `x` is not a string
if x.isupper():
# do something with the uppercased string
```
is problematic because `isupper` can only be called once we are sure that `x` is a string; this code will raise an error if `x` is a number. We could instead write
```python
# a valid but messy way to filter out non-string objects
if isinstance(x, str):
if x.isupper():
# do something with the uppercased string
```
but the more elegant and concise way of handling the nestled checking is to leverage our ability to short-circuit logic expressions.
```python
# utilizing short-circuiting to concisely perform all necessary checks
if isinstance(x, str) and x.isupper():
# do something with the uppercased string
```
See, that if `x` is not a string, that `isinstance(x, str)` will return `False`; thus `isinstance(x, str) and x.isupper()` will short-circuit and return `False` without ever evaluating `bool(x.isupper())`. This is the preferable way to handle this sort of checking. This code is more concise and readable than the equivalent nested if-statements.
<!-- #endregion -->
<!-- #region -->
<div class="alert alert-info">
**Reading Comprehension: short-circuited expressions**
Consider the preceding example of short-circuiting, where we want to catch the case where `x` is an uppercased string. What is the "bug" in the following code? Why does this fail to utilize short-circuiting correctly?
```python
# what is wrong with me?
if x.isupper() and isinstance(x, str):
# do something with the uppercased string
```
</div>
<!-- #endregion -->
## Links to Official Documentation
- [bool](https://docs.python.org/3/library/functions.html#bool)
- [Truth testing](https://docs.python.org/3/library/stdtypes.html#truth-value-testing)
- [Boolean operations](https://docs.python.org/3/library/stdtypes.html#boolean-operations-and-or-not)
- [Comparisons](https://docs.python.org/3/library/stdtypes.html#comparisons)
- ['if' statements](https://docs.python.org/3/reference/compound_stmts.html#the-if-statement)
## Reading Comprehension Exercise Solutions:
<!-- #region -->
**Conditional statements**
1\. If `my_list` is `[]`, then `bool(my_list)` will return `False`, and the code block will be skipped. Thus `first_item` will be `None`.
2\. First, check to see if `.` is even contained in `my_file`. If it is, find its index-position, and slice the string up to that index. Otherwise, `my_file` is already the file name.
```python
my_file = "code.pdf"
if "." in my_file:
dot_index = my_file.index(".")
filename = my_file[:dot_index]
else:
filename = my_file
```
<!-- #endregion -->
<!-- #region -->
**Short-circuited expressions**
The code
```python
# what is wrong with me?
if x.isupper() and isinstance(x, str):
# do something with the uppercased string
```
fails to account for the fact that expressions are always evaluated from left to right. That is, `bool(x.isupper())` will always be evaluated first in this instance and will raise an error if `x` is not a string. Thus the following `isinstance(x, str)` statement is useless.
<!-- #endregion -->