forked from SigmaQuan/Better-Python-59-Ways
-
Notifications
You must be signed in to change notification settings - Fork 0
/
item_12_avoid_else.py
135 lines (98 loc) · 3.91 KB
/
item_12_avoid_else.py
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
# Item 12: Avoid else blocks after for and while loops
# Python loops have an extra feature that is not available in most other
# programming language: you can put an else block immediately after a loop's
# repeated interior block.
for i in range(3):
print('Loop %d' % i)
else:
print('Else block!')
# Loop 0
# Loop 1
# Loop 2
# Else block!
# Surprisingly, the else block runs immediately after the loop finishes. Why
# is the clause called "else"? Why not "and"? In an if/else statement, else
# means, "Do this if the block before this doesn't happen." In a try/except
# statement, except has the definition: "Do this if trying the block before
# this failed."
# Similarly, else from try/except/else follows this pattern (see item 13: Take
# advantage of each block in try/except/else/finally) because it means, "Do
# this if the block before did not fail". try/finally is also intuitive
# because it means, "Always do what is final after trying the block before.
# Given all of the uses of else, except, and finally in Python, a new
# programmer might assume that the else part of for/else means, "Do this if
# the loop wan't completed". In reality, it does exactly the opposite. Using
# a break statement in a loop will actually skip the else block.
for i in range(3):
print('Loop %d' % i)
if i == 1:
break
else:
print('Else block!')
# Loop 0
# Loop 1
# Another surprise is that the else block will run immediately if you loop
# over an empty sequence.
for x in []:
print('Never runs')
else:
print('For Else block!')
# For Else block!
# The else block also runs when while loops are initially false.
while False:
print('Never runs!')
else:
print('While Else block!')
# While Else block!
# The rationale for these behaviors is that else blocks after loops are useful
# when you're using loops to search for something. For example, say you want
# to determine whether two numbers are coprime (their only common divisor is
# 1). Here, I iterate through every possible common divisor and test the
# numbers. After every option has been tried, the loop ends. The else block
# runs when the numbers are coprime because the loop doesn't encounter a
# break.
a = 4
b = 9
for i in range(2, min(a, b) + 1):
print('Testing', i)
if a % i == 0 and b % i == 0:
print('Not coprime')
break
else:
print('Coprime')
# Testing 2
# Testing 3
# Testing 4
# Coprime
# In practice, you wouldn't write the code this way. Instead, you'd write a
# helper function to do the calculation. Such a helper function is writen in
# two common styles.
# The first approach is to return early when you find the condition you're
# looking for. You return the default outcome if you fall through the loop.
def coprime(a, b):
for i in range(2, min(a, b) + 1):
if a % i == 0 and b % i == 0:
return False
return True
# The second way is to have a result variable that indicates whether you've
# found what you're looking for in the loop. You break out of the loop as soon
# as you find something.
def coprime2(a, b):
is_coprime = True
for i in range(2, min(a, b) + 1):
if a % i == 0 and b % i == 0:
is_coprime = False
break
return is_coprime
# Both of these approaches are so much clearer to readers of unfamiliar code.
# The expressively you gain from the else block doesn't outweigh the burden
# you put on people (including yourself) who want to understand your code in
# the future. Simple constructs like loops should be self-evident in Python.
# You should avoid using else blocks after loops entirely.
# Things to remember
# 1. Python has special syntax that allows else blocks to immediately follow
# for and while loop interior blocks.
# 2. The else block after a loop only runs if the loop body did not encounter
# a break statement.
# 3. Avoid using else blocks after loops because their behavior isn't
# intuitive and can be confusing.