What & How & Why

Control Structures

Chapter 3 notes


Control Structures

  • 控制程序执行流程的代码

Conditionals

  • 基于逻辑表达式选择不同的执行方式
  • if / else
indentation and conditionals
  • python 中所有具有相同缩进的代码被视作同一个 block 下的代码
  • 缩进也可以嵌套
  • Loops
  • Functions
  • Exceptions

scope

  • scope 指在程序执行中使变量可见的区域
  • 编译性语言和解释性语言有所不同
  • scope 通过 control structure 来体现
    • conditional 不能保证自身运行,因此其内部的变量不能对外部可见
    • function 内部的变量对外部不可见
scope in python
  • python 中的变量 scope 从变量创建的一刻开始
  • 该变量需要到下一行才能使用(不能创建中使用自身)
if / else in python
  • 条件语句下的 block 会接一个 :

condition & boolean operators

  • 可以通过 boolean 表达式来制定条件
  • boolean 表达式可以接受返回 boolean 的 function
  • boolean 运算符可以用于连接多个表达式
使用 member op 作为条件表达式

可以通过 in 成员查询条件是否属于某个条件集合:

club_members = ["John", "Jenny", "Jason", "Jane"]
my_name = "Jenny"
if my_name in club_members:
    print("Welcome back!")
in 关键字还可以用于匹配字符串,比如:
mystery_string = "zizazzle"
if "zzz" in mystery_string = "zizazzle":
    do sth....

使用 function 作为条件表达式

#isdigit() is an member function of string that used as the condition
myNumericString = "12345"
if myNumericString.isdigit():
    prtin("something")

scope 与 condition
  • 条件语句不运行的前提下,其 block 下的 scope 是不存在的。因此所有在该 scope 内部的变量都不存在。
  • 推荐在条件语句外部创建 scope (minimun level)

Loops

  • Loop: A programming control structure that executes a segment of code multiple times.
    • For loops: A loop control structure that runs a block of code a predetermined number of times.
    • Foreach: 自动循环容器中的所有内容(而不用手动指定循环次数)
    • While loops: A single execution of a repeated task or block of code.
    • do while: run at least once, then check the condition

For loop

For Loops in Python

基本写法:

for i in range(1, 11):
    print(i)
range 是左闭右开区间 $[1,11)$。range() 函数的几种初始化方法:
#0 - 9, implicitly
range(10)
#0 - 9, explicitly
range (0 , 10)
#0, 2, 4, 6, 8
range(0, 10, 2)
#count down(reverse) 10 - 1
range(10, 1, -1)

Primer on Indices
  • 循环适用于长得像 list 一样的主体k
  • 循环,index 的起点是 0
for each loop

for each loop 与 for loop 类似。但相比 for loop,for each 使用变量(list)来作为范围,for each loop 会循环 list 中所有的元素。比如:

listOfNumbers = [1,2,3,4,5]
for i in listOfNumbers:
    sum += i
这个等同于如下的 for loop:
for i in range(0, len(listOfNumbers)):
    sum += i
python 中,string 也可以作为范围变量;for each 访问的是 string 中的每一个字母:
mystring = "abcde"
for char in mystring:
    do sth

While Loop

While Loop 适用于我们不清楚循环要进行多少次的情况下。

python 的随机数

import random

# Returns a random integer greater than or equal to min and less than or equal to max.
random.randint(min, max)

nested loop

使用 nested loop 统计 list string 中有多少词:

  • 使用空格作为词数的技术
  • 在最后加上整个 list string 的长度补足未计数的部分

list_of_strings = ["This is the 1st string", "This is the 2nd string", 
                   "This is the 3rd string", "This is the 4th string", 
                   "This is the 5tj string" ]

space_count = 0

for current_string in list_of_strings:
    for curr_char in current_string:
        if curr_char == " ":
            space_count += 1

word_counts = space_count + len(list_of_strings)
print(word_counts)

相关概念

重要的 loop 关键字
  • continue:跳过当前循环中迭代剩下的内容,跳到下一个迭代
  • pass:使用 loop,但什么都不做(空语句)
  • break:跳出所有的循环
scope & loop
  • loop 中创建的变量会在下一次迭代中被重新初始化。如果不希望被重置,把变量放到循环外部。

Functions

  • Function: A segment of code that performs a specific task, sometimes taking some input and sometimes returning some output.
什么是 call 和 definitaion
  • Function Call: A place where a function is actually used in some code.
  • Function Definition: A segment of code that creates a function, including its name, parameters, and code, to be used by other portions of a program.
    • Function Header: The name and list of parameters a function expects, provided as reference to the rest of the program to use when calling the function.
      • name
      • Parameter: A variable for which a function expects to receive a value when called, whose scope is the function’s own execution.
    • Function Body: The code that a function runs when called.
    • Return Statement: The line of code that defines what output will be sent back at the end of a function.
call 的过程
  • 给 function paramater 使用的被称为 Arguments。可以将 parameter 视作 variable, Arguments 视作具体值。
  • 需要注意的三点:
    • how to call
    • what input will be given
    • what output to expect(某些 call 不会返回值)

function in python

定义 function

#defination
def printYen():
    print("$", end='')

# call
printYen()

python 中必须先定义函数再使用。

parameter & returns

# return
def returnYen():
    return "Y"

# using parameter
def returnYenAmount(amount):
    return "Y" + str(amount)
print(returnYenAmount(5))

return 会直接终止当前 function body 的继续运行

注意事项
  • not to use the same variable names in your parameter list that you use in the rest of your program
  • always make sure to check full_results.txt
常见错误
  • Parameter Mismatch:argument 和 parameter 的数量不同,或是参数不匹配。
  • Scope Error:
    • 函数内部的变量的 scope 只处于函数内部。
None
  • 如果函数不返回任何值,那么返回 None
  • 需要确保每个 conditional 中都有正确的 return (包括 nested conditional)
  • 循环结束时可能没有成功匹配条件,因此需要在 loop 结束后手动 return
keyword parameters

keyword prarameter 指函数中的自带的,用于实现某种特殊功能的可选参数(可忽略)。比如 print()

#sep is the character between string A and string B
#end is the character added at the end of the printed string
#output will be A#B!
# by default, sep = space, end = newline
print("A", "B", sep = "#", end = "!")
keyword parameter 处于所有函数 parameter 的后方,可以自定义;功能类似 C++ 的函数默认值。比如:
#currency has default value USD
def currencyAmount(amount, currency = "USD")
#when calling, currency can be changed by signing an parameter to it
currencyAmount(5, currency = "GBP")

Exception Handling

  • Exception Handling 针对的是某些可以预期的问题。这些问题通常不重要,但有时会影响到用户的使用体验。Exception Handling 可以尽可能的使用户避免这些问题。
catching” Errors
  • 如果 error 没有被捕获(uncaught error),很可能导致程序崩溃
  • 对 error 进行捕获 (catching error) 可以避免程序的崩溃

catching error 与通常避免错误的处理方式不同。通常的处理的方式是阻止错误的发生;比如不允许除零的行为,来保证程序的正常运行。而 catching error 是对 error 做处理,保证遭遇错误的时候程序依然能够正常运行:比如当用户进行除零的行为时,catching error 首先告诉电脑不要 crash,其次再通过电脑告知用户除零这种操作是不允许的。

Catch Error 的优势
  1. 对于独立的 function, catching error 允许我们首先考虑 code 本身的功能,在之后再去考虑可能产生什么样的 error
    • catching error 的方式可以允许程序执行一部分直到错误的出现
  2. 相比把所有错误检测的条件语句放到程序之前,catch error 将所有 error 的处理放到最后,使程序更易读
  3. catching error 可以在遭遇未知错误的时候阻止程序 crash

Try-Catch-Finally

  • The Try statement marks a block of code to attempt, but in which we anticipate an error might arise.
  • The Catch statement names the errors to anticipate, and marks a block of code to run if an anticipated error arises. (Python often refers to this as the Except block as well.)
  • The Finally statement marks a block of code to run after the above two blocks no matter what.

Try block 中的代码是试错用的, catch 是处理 error 用的 ,Final 中的代码是之前两步完成以后一定会运行的代码(应该放一些比较稳当的代码)

Try and Except

在 python 中, try-catch 以 try - except 关键字的形式存在。比如下列的例子:

try:
    print("Converting myString to int....")
    myInt = int(myString)
    print(myInt)
except:
    pass
print(done)

需要注意的是,如果 try block 中有任意错误出现,那么代码会直接跳转到之后的 except block 中;try block 之后的语句都不会执行。相反,如果 try block 中的语句没有出现错误,那么 except block 永远不会执行。

指定 except block 中的内容

execpt block 中可以指定对错误内容的反馈(log),比如:

try:
    print("Converting myString to int....")
    myInt = int(myString)
    print(myInt)
except:
    print("Can't convert an non-number string to int")

指定需要捕获的 error

python 中允许我们对指定类型的 error 进行捕获,比如:

try:
    print(1 / 0)
    print("No error occurred!")
except ZeroDivisionError:
    print("An error occurred!")
print("Done!")
这里的 print(1/0)ZeroDivisionError 的错误类型,因此 except block 会成功匹配该错误,并执行该错误下的语句,这里是 print(“An error occurred!”)

但需要注意的是,这种情况下,python 只能捕获 except block 中指定的错误类型。如果出现了别的类型的错误,python 会:

  1. 按通常一样,跳转到 except block
  2. 寻找是否存在与该错误类型对应的 block
  3. 如果没有找到,那么该程序会 crash
使用变量存储错误信息

如果我们希望看到一些额外的,关于错误如何抛出的信息,我们可以使用 as 关键字来存储该信息。写法如下:

try:
    print(1 / 0)
except ZeroDivisionError as error:
    print(error) # variable error stores error info from ZeroDivisionError 
print("Done!")
此处的 error 变量可以为任何名字,存储的是 ZeroDivisionError 带来的信息 division by zero。因此,打印结果为:
division by zero
Done!

同时处理多种 error

python 提供了同时处理多种 error 的机制:

try:
    print("String" + 1)
    print(1 / 0)
except ZeroDivisionError:
    print("A ZeroDivisionError occurred!")
except TypeError:
    print("A TypeError occurred!")
print("Done!")
可以注意到, print(“String” + 1) 会导致 TypeError. 当该错误出现时,python 会跳转到 except block 中,以从上到下的顺序寻找与该错误类型匹配的 except block。我们的程序中有对应 TypeError 的处理,因此会打印:
A TypeError occurred!
Done!
需要注意的是,即便同时存在多种类型的错误,只要成功捕获了一个错误,python 就会执行对应的 except block 中的内容,以及 except block 之后的内容了。即便是 try 中还存在着其他类型的错误,python 也不会在进行执行,比如:
try:
    print("String" + 1)
    myInt = int("This is string")
    print(1 / 0)

except TypeError:
    print("A TypeError occurred!")
except ZeroDivisionError:
    print("A ZeroDivisionError occurred!")
print("Done!")
这里的 myInt = int(“This is string”)ValueError 类型的错误。按道理来说,由于我们没有提供对应的错误处理,程序应该 crash 掉。但实际上,在其执行之前已经出现了 TypeError 类型的错误,因此该行代码以及其之后的,在 try block 中的内容都不会执行。因此打印结果与之前的例子一样。

如果需要对其他剩余的错误做一个总的处理,可以使用关键字 Exception,比如
try:
    myInt = int("This is string")
    print("String" + 1)
    print(1 / 0)

except TypeError as error:
    print(error)
except ZeroDivisionError as error:
    print(error)
except Exception as error:
    print(error)
print("Done!")
如果不指定具体打印内容,python 会自动根据具体的错误类型信息将错误打印出来:
Invalid literal for int() with base 10: 'This is string'
Done!

Else and Finally

else 关键字

else 关键字是 python 特有的一个机制。当所有其他 except block 中的内容都没有执行的时候(也就是 try block 中程序无错运行完毕的时候),python 会跳转到 else block 下的语句运行,比如:

mystery_value = 9

try:
    print( 10 / mystery_value)
except ZeroDivisionError:
    print("Can't divide by zero")
except Exception:
    print("Not possible")
else:
    print("No error found!")
将打印:
1.1111111111111112
No error found!

else 关键字可能看起来有些多余。但实际上,因为这个关键字属于 except block 的一部分,而只有当所有 expect block 都没有执行,也就是没有错误被捕获的时候,else block 才会运行。因此,当 else block 中的语句被执行时,我们就可以百分之百的确认之前 try block 里的代码是没有(非逻辑)错误的。

else 的重要应用:读取文件

该应用由如下程序所示:

try:
    #open file in read only mode
    input_file = open("Fake.txt", mode = "r")
except IOError as error:
    print("An input error occurred!")
else:
    for line in input_file:
        print lines
    input_file.close()
该应用中,如果在打开 Fake.txt 的过程中出现了任何错误,都会被 python 捕获。如果打开正确,则会执行剩余的逐行读取操作(else block 中的部分)。

Finally 关键字

无论 try block 中有没有内容被捕获,Finally block 中内容都会运行:

try:
    #open file in read only mode
    input_file = open("Fake.txt", mode = "r")
except IOError as error:
    print("An input error occurred!")
else:
    for line in input_file:
        print lines
finally:
    input_file.close()
这里的 input_file.close() 一定会被执行。

为什么不在 try block 后面直接加无论如何都想要运行的代码?

finally 可以保证即便是 try block 中出现无法处理的错误(Uncaught Errors)的情况下,也能继续运行 finally block 内的语句。如果直接在 try block 后面添加语句,当出现 Uncaught Errors 时,程序会直接 crash,因此后面的语句也没有机会执行了。

使用嵌套的 try block 处理不同的错误

有如下代码:

try:
    #Open InputFile.txt in read-only mode
    input_file = open("FakeFile.txt", mode = "r")  
    try:
        #For each line in the file
        for line in input_file:  
            #Print the line
            print(int(line)) 
    #Catch a ValueError
    except ValueError as error:    
        print("A value error occurred!")
    else:
        print("No errors occurred converting the file!")
    finally:
        #Close the file
        input_file.close()
#Catch an IOError
except IOError as error:
    print("An error occurred reading the file!")
这段代码中,我们的本意是先验证文件是否能正常打开,再验证文件的内容是否能正确的从 string 转化为 int。对此,我们可以使用嵌套的 try block 结构来处理。可以看到,外部的 try block 处理的是 IOError,而内部的 try block 处理的是 ValueError。几点需要注意的是:

  • 当外部 try block 捕获匹配的内容时,会直接跳过其之后的内容到对应的同级 except block 中。以本例为例子中,外部的 try block 并没有做 finally block 的处理。如果外部 try block 遇到 Uncaught Errors 时,内部的 finally block 中的语句时无法打印的。
  • 当内部的 try block 捕获错误时,其错误匹配按由内而外的顺序来搜寻。也就是:
    • 优先查找同级 except block 中有没有匹配的错误处理
    • 如果没有,回到上级 try block 对应的 except block 中查找
return 与 try block 的配合使用

return 可以终止函数的运行并返回结果。我们可以将这个功能放置在 except block 中来达到终止函数的运行并返回错误信息的效果:

def return_file_text(filename):
     try:
         file = open(filename)
     except:
         return "Error opening file!"
     file_text = file.read()
     return file_text

Error Handling and Control Structures

Error Handling and For Loops

如果 loop 出现问题就希望结束 loop,那么应该把 try block 包含整个 loop:

try:
    #For each line in the file
    for line in input_file:  
        #Print the line
        print(int(line)) 
#Catch a ValueError
except ValueError as error:    
    print("A value error occurred!")
如果希望对单次的 iteration 进行错误捕获,则应该把 try block 放到 loop 中:

#For each line in the file
for line in input_file:  
    try:
        #Print the line
        print(int(line)) 
        #Catch a ValueError
    except ValueError as error:    
        print("A value error occurred!")
这样的结构只会在出现错误时打断当前的迭代,loop 还会继续执行剩余的部分。

可以在 except block 中使用 break 语句强制结束循环

Error Handling and Functions
  • 如果 function 中出现错误,那么会首先查找 function 内部是否有 except block
  • 如果没有,会跳转到外部 function 被调用的位置,查看该位置是否处于 try block 内部。如果处于,则查找是否存在对应的 except block。