Python Lesson IV

Author: sandyzikun

简单说说Python中的流程控制, 可以大致分为如下几类:

  • 条件控制
  • 循环控制
  • 异常捕捉

Conditions

如上动画1便是条件控制的运行过程.

有的时候, 为了应对不止两个分支的情况, 我们使用elif来在不满足if对应的条件的情况下继续判断与执行程序:

Python
1
2
3
4
5
6
if a % 2 == 0:
print(a, "is even")
elif a % 3 == 0:
print(a, "is odd and can be devided by 3")
else:
print(a, "is odd and cannot be devided by 3")

这段程式中使用elif进行进一步判断, 如若if对应的条件a % 2 == 0不为真, 那么会进一步判断elif所对应的条件a % 3 == 0.

  • 如若a==56则如上程式中a % 2 == 0这一点是满足的,
    那么解释器便不会考虑下面的其他情况, 直接运行相应的程式,
    在这里便是输出:

    1
    56 is even
  • 如若a==39则如上程式中a % 3 == 0这一点是满足的,
    所以根据上述程式, 解释器会运行并输出如下:

    1
    39 is odd and can be devided by 3
  • 如若在else之前的情况皆不满足, 例如a == 5时, 解释器会运行输出:

    1
    5 is odd and cannot be devided by 3

总结一下使用if进行条件判断的用法, 便可以简单理解为:

Python
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
if <conditon 1>:
(operations) # 如若满足 condition 1,
(operations) # 则 condition 1 (的布尔值)为真,
(operations) # 则运行这些语句.
(operations) # ----否则考虑下面的条件

elif <conditon 2>:
(operations) # 如若满足 condition 2,
(operations) # 则 condition 2 (的布尔值)为真,
(operations) # 则运行这些语句.
(operations) # ----否则考虑下面的条件

elif <conditon 3>:
(operations) # 如若满足 condition 3,
(operations) # 则 condition 3 (的布尔值)为真,
(operations) # 则运行这些语句.
(operations) # ----否则考虑下面的条件

...

elif <conditon m>:
(operations) # 如若满足 condition m,
(operations) # 则 condition m (的布尔值)为真,
(operations) # 则运行这些语句.
(operations) # ----否则考虑下面的条件

else:
(operations) # 如若上述的 condition 1~m 皆不满足,
(operations) # 则一定会运行这些语句.

Loops

如上动画2便是循环控制的运行过程.

while循环结构的格式类似于如下:

Python
1
2
3
4
while <condition>:
(operations) # 如若满足 condition,
(operations) # 则会运行这些语句,
(operations) # 直到不再满足 condition.
  • 同时我们也可以发现, 在Python中进行无限循环的方法很简单, 那便是

    Python
    1
    2
    while True:
    ...

    为什么要提无限循环? 并不是我们真的想要让解释器无限地重复同一段代码直到机器报废自己退休甚至是下一次小行星重装地球, 而是因为有时在循环体内执行的操作很多, 退出循环体的需求也有很多, 此时便需要在所有需要退出的位置使用break, 这也是我们稍后便要讲的.

除了while之外, 我们还可以使用for结构来进行循环, 其格式如下

Python
1
2
3
4
for <item> in <iterable>:
(operations) # 对于 iterable 中的,
(operations) # 则会运行这些语句,
(operations) # 直到不再满足 condition.

这个结构比起说是循环, 更像是在遍历, 因为for ... in ...:的意义为”对于某序贯中的每一项, 执行相应的重复操作”, 所以我们每次需要在<item>的位置取临时变量名, 代表从<iterable>中取用的每一个变量, 取执行循环体中的操作.

break Usages

breakPython中的关键字, 用于提前结束正在进行的循环.

Python
1
2
3
4
5
for k in range(39):
if k >= 10:
print("Interrupted!!")
break
print(k, end=", ")

如上程式的输出如下

1
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, Interrupted!!

其本应当输出从038, 但是只输出到9, 便是因为在k == 10的时候它满足条件k >= 10, 触发了break, 于是提前结束.

Exceptions

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import urllib.request, urllib.error

for eachyear in [2015, 3939]:
try:
f = urllib.request.urlopen("https://magicalmirai.com/%s/" % eachyear)
except urllib.error.HTTPError as err:
print("HTTPError Occurred!", end=" ")
if err.code == 404:
print("The Page /%s/ wasnot Found!" % eachyear)
elif err.code == 403:
print("The Page /%s/ Forbade to be Visited!" % eachyear)
else:
print("which was Unknown!")
else:
print("Visited the Page /%s/ Successfully!" % eachyear)
finally:
print("Visiting of the Page /%s/ Ends!" % eachyear)

通过上面这个例子, 我们可以很清楚地看出Python进行异常捕捉的思路: 先进行试探性质的操作(try部分), 然后列举需要捕捉的异常(except部分), 如若程序在没有捕捉到任何异常的基础上, 正常地执行下去, 那么执行else部分的操作, 最后也有不论如何执行都要进行的操作(finally部分).
当然, 如若中途出现了没有捕捉的异常, 那么程式也会被中断!

except关键字可以用于捕捉执行程序过程中出现的异常, 其语法用Python的风格可以写作:

1
2
3
4
5
except [SomeError [as alias]]:
(operations)
(operations)
...
(operations)

如上, 没有任何附加参数时, 单独的except可以捕捉任何异常, 此外可以捕捉指定的异常, 如上例中的except urllib.error.HTTPError(就是HTTP协议相关的异常, 这个有机会的话会在后面网络信息采集里面进行讲解), 当且仅当发生了HTTPError及其子类时这段except会被激活并对其进行处理, 另外还可以使用as来对捕捉的异常取别名, 方便在处理过程中调用, 例如上例中用err代指HTTPError, 用于读取HTTPError发生时对应的HTTP状态码(就是404 Not Found或者403 Forbidden那一套, 关于这个有个概念就好以后再细讲).

因此我们可以得到两种可以捕捉任何异常的方法:

1
2
3
4
5
6
7
8
9
10
11
12
# Solution 1.
except:
(operations)
(operations)
...
(operations)
# Solution 2.
except Exception: # also `except Exception as err` or some other aliases ...
(operations)
(operations)
...
(operations)

但这两者有着本质上的区别!!: 前者是触发except捕捉异常的基础功能, 后者是因为所有异常都是Exception的子类!

else Usages

else这个关键字我们可以在很多计算机语言中见到, 不仅是C/C++Python, 包括R语言在流程控制中我们也可以见到

R
1
2
3
4
5
6
x <- c("miku", "ichika", "saki")
if ("miku" %in% x) {
print("Word miku Found in the List!")
} else {
print("Word miku not Found.")
}

(其实文言里面也有若非代替else…)

文言
1
2
3
4
5
若三不大於五者。
乃得「「想當然耳」」。
若非。
乃得「「怪哉」」。
也。

然而, 大部分语言中的else只用于狭义的条件语句, 而在Python中, 其还可以用于循环结构或异常处理.
方才已经展示了异常处理中的else的用法, 现在我们来看循环结构中使用else:

Python
1
2
3
4
for k in range(5):
print("MIKU!", end=" ")
else:
print("Finished!")

此时解释器会输出:

1
MIKU! MIKU! MIKU! MIKU! MIKU! Finished!

我们可以发现: 当循环运行结束之后 , else部分的操作会被执行, 然而,

Python
1
2
3
4
5
6
for k in range(5):
if k >= 3:
break
print("MIKU!", end=" ")
else:
print("Finished!")

此时解释器会输出:

1
MIKU! MIKU! MIKU! 

当循环提前结束的时候, else部分便不会再执行.

我们综合上面看到的几种情况, 可以把else的性质总结如下: elsePython中是一个关键字, 除了在if引导的条件结构外, 可以用于循环结构和异常处理, 当且仅当这类过程没有被break或抛出的异常打断之时.

Assertions

上面提到了Python的异常及其捕捉系统, 事实上异常本身并非坏事! 这是一种用于判断程式执行中错误并紧急退出, 以免导致更大危险的处理方式. 在我们编写程式时, 也应考虑可能发生的错误, 在无法补救的时候抛出异常, 中断程式.
最简单的方式便是使用raise关键字:

Python
1
>>> raise Exception

如若某个条件触发了这个异常的抛出, 程式则会中断并显示:

1
2
3
4
5
6
---------------------------------------------------------------------------
Exception Traceback (most recent call last)
<ipython-input-1-2aee0157c87b> in <module>()
----> 1 raise Exception

Exception:

不同的异常, 不论是哪个, 其归根结底皆为对象, 皆为类的实例化产物.
因此在抛出异常的时候, 对于大多数异常类, 需要使用构造函数对其进行初始化(但愿大家都学过面向对象).
如上的Exception之所以可以直接抛出, 是因为其可以不指定参数调用构造函数进行初始化, 但不代表所有异常都可以这样抛出.
如下例中的urllib.error.HTTPError便需要如上所言如一般类的实例化一样, 调用构造函数:

1
2
>>> import urllib.error 
>>> raise urllib.error.HTTPError("https://magicalmirai.com/3939/", 404, "Not Found", None, None)

有的时候, 在我们撰写类或函数的时候, 需要对使用者传入参数的变量进行限制, 此时便需要用到断言(assert)机制:

Python
1
2
3
def transposition(arr: list):
assert isinstance(arr, list) and len(arr) == 1 and isinstance(arr[0], list), "Ensure the Array Input is a Row-vector!"
return [ [each] for each in arr[0] ]

此时便会限定调用者传入的参数是一个形如[[ 3, 9 ]]的嵌套列表, 如若不满足条件便会抛出异常如下:

1
2
3
4
5
6
7
8
9
10
11
---------------------------------------------------------------------------
AssertionError Traceback (most recent call last)
<ipython-input-22-ba22e8120b98> in <module>()
----> 1 transposition([ 3, 9 ])

<ipython-input-21-1ce3e718177c> in transposition(arr)
1 def transposition(arr: list):
----> 2 assert isinstance(arr, list) and len(arr) == 1 and isinstance(arr[0], list), "Ensure the Array Input is a Row-vector!"
3 return [ [each] for each in arr[0] ]

AssertionError: Ensure the Array Input is a Row-vector!

结合上面所说的异常的基本特征, 我们可以总结如下:

1
2
3
4
assert <condition>, [message]
# <=>
if not <condition>:
raise AssertionError([message])

这两者等价.

Attentions!!

我们有时会在C/C++中看到如下被称为do-while的循环结构

C++
1
2
3
4
5
6
do {
operations; // 不论如何先把这些代码做一遍,
operations; // 然后再判断是否满足`condition`即其(布尔值)为真,
operations; // 如若`condition`为真则继续循环否则退出.
...;
} while (condition);

这个在Python中是从来都不存在的, 而且你如若需要的话可以在进入循环之前单独写一遍你所需要的过程.

References

1. 菜鸟教程 Python3 条件控制
2. 菜鸟教程 Python3 循环语句