# 3. 流程語法與函式
## 3.1 流程
程式執行的三種邏輯:「循序」、「判斷」與迴圈,程式不會永遠從頭到尾全部執行,而是會因為時間、情況或是使用者的操作而產生不同的執行結果,因此有了「判斷」因狀況不同而執行不同的程式碼或是重複執行相同一段程式碼數次。
### 3.1.1 `if`分支判斷
格式:
> ```
> if 條件式:
> 條件成立執行的程式碼
> ```
> 注意:流程內的程式碼一定要縮排。
分支判斷可以用來根據判斷來決定是否要執行某一段程式碼,例如:
```
import datetime
if datetime.date.today().weekday() != 0:
print('今天不是Blue Monday.')
```
1. 呼叫`datetime.date.today().weekday()`會回傳今天是星期幾,0 = 星期一,6 = 星期日,中間以此類推。
2. 因為`datetime`是Python內建的外部模組,所以必先`import datetime`這個模組。
3. 記得在`if`判斷式的最後加上冒號「:」代表是判斷式區塊程式碼的開頭,接下來區塊程式碼必須全部縮排一個TAB或至少一個空白以上(建議是4個空白字元),每一行都必須對齊,也不可以混用TAB與空白字元,否則會出現錯誤無法執行。
4. 如果沒有縮排,代表該程式碼不屬於`if`判斷式區塊內的程式碼。
```
import datetime
import random
print(datetime.date.today().weekday()) # 取得星期, 從0開始代表星期
weekday = datetime.date.today().weekday()
if weekday == 5:
print('今天星期六')
input('輸入y開始算命:')
# 隨機取得1~3之間整數
n = random.randint(1, 3)
if n == 1:
print('恭喜,下周一買威力彩會中獎.')
if n == 2:
print('今天出門要注意安全')
if n == 3:
print('今天會撿到錢')
```
###### 多重分支
格式:
> if 條件1:
> 條件1成立執行的程式碼
> elif 條件2:
> 條件2成立執行的程式碼
> ...
> elif 條件n:
> 條件n成立執行的程式碼
> else:
> 所有條件都不成立要執行的程式碼
```
score = int(input('請輸入成績: '))
if score >= 90:
print('讚')
elif 90 > score >= 80:
print('不錯,再加油!')
elif 80 > score >= 70:
print('還可以')
elif 70 > score >= 60:
print('及格邊緣')
else:
print('不及格,要多加油')
```
會輸出:
```
請輸入成績: 100
讚
```
##### if...else
如果只是單純的判斷後結果只有兩種「是」或「不是」,Python有個更簡潔的語法,例如:
```
b = 5
print('偶數' if b % 2 == 0 else '奇數')
```
判斷是成立,會回傳`if`前面的值,否則回傳`else`後面的值,等同於:
```
b = 5
if b % 2 == 0:
print('偶數')
else:
print('奇數')
```
更多範例:
```
print('判斷===============================')
a = -5
if a > 0:
print('條件成立1')
print('條件成立2')
print('條件成立3')
print('XXXXX')
import datetime
import random
print(datetime.date.today().weekday()) # 取得星期, 從0開始代表星期
weekday = datetime.date.today().weekday()
if weekday == 5:
print('今天星期六')
start = input('輸入y開始算命:')
if start == 'y':
# 隨機取得1~3之間整數
n = random.randint(1, 3)
# if n == 1:
# print('恭喜,下周一買威力彩會中獎.')
# if n == 2:
# print('今天出門要注意安全')
# if n == 3:
# print('今天會撿到錢')
if n == 1:
print('恭喜,下周一買威力彩會中獎.')
elif n == 2:
print('今天出門要注意安全')
elif n == 3:
print('今天會撿到錢')
else:
print('系統錯誤')
else:
print('取消算命')
print('下午------------------------------------------')
user_data = int(input('請輸入一個數字:'))
# if user_data % 2 == 1:
# print(user_data, '是奇數')
# if user_data % 2 == 0:
# print(user_data, '是偶數')
# if user_data % 2 == 1:
# print(user_data, '是奇數')
# else:
# print(user_data, '是偶數')
print('奇數' if user_data % 2 == 1 else '偶數')
n = random.randint(1, 5)
user_data = int(input('請猜一個1~5之間的數字:'))
# if n == user_data:
# print('Bingo')
# if n != user_data:
# print('答案是:', n)
# if n == user_data:
# print('Bingo')
# else:
# print('答案是:', n)
print('Bingo' if n == user_data else '答案是:', n)
weekday = datetime.date.today().weekday() # 取得今天星期幾
if weekday == 0:
print('今天是星期一')
elif weekday == 1:
print('今天是星期二')
elif weekday == 2:
print('今天是星期三')
elif weekday == 3:
print('今天是星期四')
elif weekday == 4:
print('今天是星期五')
elif weekday == 5:
print('今天是星期六')
elif weekday == 6:
print('今天是星期日')
else:
print('今天是芥末日')
```
##### 練習
1. 寫一程式,使用者可以輸入一個整數,然後告訴使用者輸入的是奇數還是偶數。
2. 寫一個猜數字遊戲,電腦隨機產生1~5之間的數字,然後讓使用者猜,猜到則回應:Bingo,沒猜到則直接顯示答案。
3. 開發一個猜拳遊戲,由使用者輸入0=剪刀,1=石頭,2=布,電腦則隨機產生0~2數字後來跟使用者比較,並回應是電腦贏還是玩家贏或是平手。
### 3.1.2 `while`迴圈
格式:
> ```
> while 條件式:
> 程式碼1
> 程式碼2
> ...
> 程式碼n
> ```
範例:
```
import random
n = 0
while n != 1: # 如果不是1就會跑迴圈
n = random.randint(0, 9) # 產生0到9的整數
print(n)
print('出現1了,結束迴圈.')
```
輸出範例:
```
8
8
1
出現1了,結束迴圈.
```
更多範例:
```
i = 0
while i <= 20:
print(i)
i += 1
print('迴圈結束')
n = int(input('您想要算多少的階層: ')) # 使用者想要算的階層
index = 1 # 階層的起點
result = 0 # 階層的總和
while index <= n:
result += index
index += 1
print(n, '的階層為:', result)
```
印出list內所有元素:
```
a = [3, 4, 5, 6, 7]
i = 0
while i < len(a):
print(a[i])
i += 1
```
九九乘法:
```
i = 1
while i <= 9:
print('2 x ', i, '=', 2*i)
i += 1
```
輸出為:
```
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
2 x 4 = 8
2 x 5 = 10
2 x 6 = 12
2 x 7 = 14
2 x 8 = 16
2 x 9 = 18
```
畫出6x6正方形:
```
i = 0
while i < 6:
j = 0
while j < 6:
print('*', end='')
j += 1
print('')
i += 1
```
##### 練習:
1. 使用者輸入一數字,然後畫面上輸出相同數量的*
2. [3, 9, 87, 168, 100],請使用while迴圈計算該list內元素總和。
3. 請使用while迴圈輸出完整的九九乘法表。
例如:
```
2 x 1 = 2
2 x 2 = 4
2 x 3 = 6
2 x 4 = 8
2 x 5 = 10
2 x 6 = 12
2 x 7 = 14
2 x 8 = 16
2 x 9 = 18
3 x 1 = 3
3 x 2 = 6
3 x 3 = 9
3 x 4 = 12
3 x 5 = 15
3 x 6 = 18
3 x 7 = 21
3 x 8 = 24
3 x 9 = 27
...
9 x 1 = 9
9 x 2 = 18
9 x 3 = 27
9 x 4 = 36
9 x 5 = 45
9 x 6 = 54
9 x 7 = 63
9 x 8 = 72
9 x 9 = 81
```
抽籤範例:
```
import random
students = ['Aaron', 'Apple', 'Amber', 'Astrid', 'Andy', 'Abner', 'Alan']
while len(students) > 0:
print('參與學生: ', students)
input('輸入y開始抽:')
i = random.randint(0, len(students) - 1)
print("--------------------\n恭喜", students[i], "被抽中了!!\n--------------------\n")
del students[i]
print('結束了.')
```
### 3.1.3 `for in` 迭代
如果想要從字串、list、tuple、set或是map中一個一個值取出來做處理(此作法又稱為迭代),可以使用for in迴圈。
格式:
> ```
> for 變數 in 要迭代的群集:
> 處理迭代出來的變數
> ...
> ```
例如迭代list:
```
data = [1, 2, 3]
for e in data:
print(e)
```
會輸出:
```
1
2
3
```
說明:
for in迴圈會迭代data群集變數裡的每個值,一個一個取出來,放到e這個變數裡,迴圈每跑一次就會取出一個值放到e變數。
```
print('for in --------------------------------------')
for x in a:
print('迭代:', x)
b = (3, 5, 6, 8, 9)
for x in b:
print('迭代tuple', x)
c = {'A': 0, 'B': 1, 'C': 2}
for x in c:
print('迭代map的keys', x)
for x in c.values():
print('迭代map的values', x)
for x, y in c.items():
print('迭代map', x, ':', y)
print('取出偶數----------------')
a = (1, 4, 99, 125, 88)
for b in a:
if b % 2 == 0:
print(b)
b = {3, 8, 8, 9}
total = 0
for x in b:
total += x
print(total)
```
##### range()函式
range()函式用來產生數字,格式為:
> ``` range(start, stop[, step])```
>
> start省略時預設為0,step省略時預設為1
例如:
```
range(5) # 產生0~5的數字
range(1, 6, 2) # 產生1, 3, 5
```
##### zip()函式
zip()函式用來將兩個序列內的元素,像拉鍊一樣一對一配對起來並產生一個新的list,每個配對的元素為tuple,例如:
```
a = [1, 2, 3]
b = ['one', 'two', 'three']
c = zip(a, b)
print(list(c))
```
會輸出:
```
[(1, 'one'), (2, 'two'), (3, 'three')]
```
```
a = ['小明', '小白', '小黃']
b = [200, 234, 278]
for item in zip(a, b):
print(item)
for name, grade in zip(a, b):
print(name, ' = ', grade)
```
輸出:
```
('小明', 200)
('小白', 234)
('小黃', 278)
小明 = 200
小白 = 234
小黃 = 278
```
學校有學生 小明, 小白, 小黃, 小花, 小A參加歌唱比賽,需要抽籤決定順序,請幫忙寫一程式來公平的決定順序
輸入範例:
```
小明 : 6
小白 : 2
小黃 : 3
小花 : 5
小A : 1
小莉 : 4
```
程式碼如下:
```
print('抽籤決定順序------------------------')
'''
'''
import random
students = ['小明', '小白', '小黃', '小花', '小A', '小莉']
seqs = list(range(1, len(students) + 1 ))
random.shuffle(seqs)
for name, seq in zip(students, seqs):
print(name, ': ', seq)
```
##### enumerate()函式
迭代時會產生序列的索引值,例如:
```
b = ['one', 'two', 'three']
for e in enumerate(b):
print(e)
```
結果為:
```
(0, 'one')
(1, 'two')
(2, 'three')
```
##### 練習
1. 請用迭代取出`a = [1, 4, 99, 125, 88]`並顯示在畫面上。
### 3.1.4 流程改變
#### `pass`
如果在某個程式碼區塊不想做任何事情,或是之後才會寫些程式碼,就可以先放個`pass`,例如:
```
if is_ok:
print('完成')
else:
pass
```
`pass`什麼事情都不會做,就只是用來維持程式碼結構的完整性。
#### `break`
用來跳離迴圈,迴圈內只要遇到`break`都會立即結束迴圈。
#### `continue`
用在迴圈內,在迴圈內只要遇到`continue`就會直接忽略迴圈內剩下的程式碼,馬上進行下一次的迴圈(或迭代)。
例如:
```
text = input('請輸入英文單字: ')
for t in text:
if t.isupper():
continue
print(t, end='')
```
會輸出:
```
請輸入英文單字: AbCdEf
bdf
```
範例:
```
print('pass------------------------------------')
a = 5
if a == 1:
print('好')
elif a == 2:
print('很好')
elif a == 3:
pass
elif a == 4:
print('可以')
else:
print('都行')
print('break & continue----------------------------------------')
a = 0
while a < 10:
a += 1
if a == 5:
break
print(a)
print('===')
a = 0
while a < 10:
a += 1
if a == 5:
continue
print(a)
while True:
a = input('請輸入quit結束迴圈')
if a == 'quit':
break
# 請將使用者輸入的字串過濾掉大寫
text = input('請輸入英文字串: ')
for c in text:
if c.isupper(): # 判斷c裡面的英文字是否為大寫
continue
print(c, end='')
```
### 3.1.5 `for` Comprehension
將一個list經過計算轉換後存到另一個list是很常見的操作,Python針對這樣的動作提供了更簡潔的語法,for Comprehension。
例如:將一個list內的值全部平方
一般寫法:
```
a = [1, 2, 3, 4, 5]
b = []
for e in a:
b.append(e ** 2)
print(b)
```
輸出:
```
[1, 4, 9, 16, 25]
```
for Comprehension寫法:
```
a = [1, 2, 3, 4, 5]
b = [e ** 2 for e in a]
print(b)
```
##### 結合條件式
例如:取得list中的偶數存到新的list
原本的寫法:
```
a = [3, 7, 88, 123, 20, 204, 99]
b = []
for e in a:
if e % 2 == 0:
b.append(e)
print(b)
```
輸出:
```
[88, 20, 204]
```
for Comprehension寫法:
```
a = [3, 7, 88, 123, 20, 204, 99]
b = [e for e in a if e % 2 == 0]
print(b)
```
##### sum()函式
計算指定序列內數字的加總,例如:
```
a = sum([1, 2, 3])
print(a)
```
會得到6
##### 練習
1. 寫一程式,計算1加到10000的總和。
## 3.2 函式
已經寫程式也有一段時間了,可能會發現有些程式碼可以重複利用,因此常常會「複製」、「貼上」,常常有些算式只是變數的不同,但算式是一樣的,這時候我們就可以封裝成函式來重複使用,好處有:
1. 減少程式碼,讓程式更容易維護。
2. 增加程式碼的閱讀性。
3. 更有效率的的程式開發。
例如:
```
a = 1
b = 2
c = 3
d = 4
if a > b:
print(a, '比', b, '大')
else:
print(b, '比', a, '大')
if c > d:
print(c, '比', d, '大')
else:
print(d, '比', c, '大')
```
改用函式會變成:
```
a = 1
b = 2
c = 3
d = 4
def compare(n1, n2):
if n1 > n2:
print(n1, '比', n2, '大')
else:
print(n2, '比', n1, '大')
compare(a, b)
compare(c, d)
```
九九乘法表:
```
# 非函式版
for j in range(2, 10):
for i in range(1, 10):
print(j, ' x', i , ' = ', j * i)
# 函式版
def ninenine(x):
for i in range(1, 10):
print(x, ' x', i , ' = ', x * i)
for i in range(2, 10):
ninenine(i)
```
函式是一種流程上的抽象,封裝了實作細節,例如上面範例,以後如果要進行更多比較,只要呼叫`compre()`函式就可以了,而不需要管函式內是怎麼實作的。
### 3.2.1 定義函式
使用關鍵字`def`來定義函式,格式如下:
> ```
> def 函式名稱(參數1, ..., 參數n):
> 程式碼區塊
> ...
> ```
注意:
1. 函式內程式碼一定要縮排。
2. 函式名稱第一個字只能是英文或底線。
3. 保留字不能當成函式名稱。
4. 函式名稱內的大小寫是不一樣的。
避免使用的命名:
1. 除了計數器及迭代器內之外,不要用單一字母為變數命名,請使用有意義的名稱。
2. 變數名稱前後不要加上兩個雙底線,雙底線的變數有特殊含義。
3. package 及 module 的名稱中不要包含破折號 "-"
### 3.2.2 參數與引數
在呼叫函式的時候,可以從外部傳入資料讓函式處理,例如:
```
def say_hello(text):
print('Hello', text)
```
##### 參數預設值
在參數中可以設定預設值,如果沒有傳入該參數,就會自動以預設值來帶入,例如:
```
def call(name, num = '錯誤,沒有輸入號碼'):
print('撥電話給: ', name, '號碼:', num)
call('Aaron', '0968123456')
call('Amber')
```
會輸出:
```
撥電話給: Aaron 號碼: 0968123456
撥電話給: Amber 號碼: 錯誤,沒有輸入號碼
```
> 有參數預設值的參數不可以在沒有參數預設值的參數前面。
##### 關鍵字引數
在傳入參數時,可以使用關鍵字引數,就可以不需要照參數的順序傳入,例如:
```
def call(name, num = '錯誤,沒有輸入號碼'):
print('撥電話給: ', name, '號碼:', num)
call(num = '0968123456', name = 'Aaron')
```
### 3.2.3 回傳值
當函式執行完後,如果會產生結果,可以透過`return`關鍵字來將結果值回傳給呼叫者,例如:
```
def plus(a, b):
c = a + b
return c
result = plus(3, 4)
print('總合: ', result)
```
會輸出:
```
總合: 7
```
> 函式內如果遇到`return`陳述句,就會馬上結束函式執行並跳離函式。
### 3.2.4 lambda
##### 函式也是物件
在Python裡函式也是物件,也就是說可以被放到變數裡傳遞,例如:
```
def call(name, num = '錯誤,沒有輸入號碼'):
print('撥電話給: ', name, '號碼:', num)
make_call = call
make_call('Aaron', '0999000123')
```
在很多情況下,函式內可能只是很簡單判斷式,這時我們就可以用更簡潔的語法`lambda`來表示,例如:
```
def max(n1, n2):
if n1 > n2:
return n1
return n2
```
使用`lambda`為:
```
max = lambda n1, n2: n1 if n1 > n2 else n2
```
都可以做這樣的呼叫:
```
print(max(3, 4))
```
### 3.2.5 變數作用域
**變數可以在三個不同的地方分配**
1. 如果一個變數在def內賦值,它被定位在這個函式之內
2. 如果一個變數在一個巢狀的def中賦值,對於巢狀函式來說,他是非本地的
3. 如果在def之外賦值,他就是整個檔案全域性的
作用域法則
1. 內嵌模組是全域性作用域
2. 全域性作用域的作用範圍僅限於單個檔案
3. 每次對函式的呼叫都建立了一個新的本地作用域
4. 函式中賦值的變數名除非宣告為全域性變數或非本地變數,否則均為本地變數
5. 所有其他的變數名都可以歸納為本地,全域性或者內建的
變數名解析
1. 變數名引用分為三個作用域進行查詢,首先是本地,然後是上層函式的本地作用域,之後全域性,最後是內建
2. 預設情況下,變數名賦值會建立或者改變本地變數
3. 全域性宣告和非本地宣告將賦值的變數名對映到模組檔案內部的作用域
## COVID-19 冠狀病毒程式開發
### 專有名詞
#### API - Application Program Interface
API 是應用程式介面(Application Programming Interface)的簡稱。開發者們能透過 API 將彼此的服務與資料相互整合,不僅增進開發的效率,也減少重複工作的現象發生。隨著軟體逐漸吞噬這個世界,用來整合不同軟體,並提供便利服務的 API 的重要性也就日漸提升。
#### JSON - Javascript Object Notation
Javascript物件表示法,一種輕量的資料交換格式,常用來作為網路API資料交換用。
例如:
```
{
"status": "ok",
"data": {
"1": "Taiwan",
"2": "Japan",
"3": "USA"
},
"assets": [1, 2, 3, 4, 5]
}
```
> 1. 其資料結構與Python的dictionary相同>
> 2. 每筆資料都必須用逗點[,]隔開。
> 3. Key必須是由雙引號包住的字串。
> 4. Value可以是字串、數字、布林值或陣列等等。
#### HTTP狀態碼
API呼叫後都會回傳一個狀態碼,用來說明執行的成功與否;HTTP狀態碼表明一個 HTTP 要求是否已經被完成,回應分為五種:
- 資訊回應 (Informational responses, 100–199),
- 成功回應 (Successful responses, 200–299),
- 重定向 (Redirects, 300–399),
- 用戶端錯誤 (Client errors, 400–499),
- 伺服器端錯誤 (Server errors, 500–599).
#### [RapidAPI](https://rapidapi.com/)
API 市集平台,核心功能,是協助開發者與 API 服務供應商之間進行媒合。
- [RapidAPI-COVID-19網頁](https://rapidapi.com/KishCom/api/covid-19-coronavirus-statistics)
- [將JSON字串格式化成容易閱讀](https://codebeautify.org/jsonviewer)
> 貼上JSON文字後,點選【Beautify按鈕】。
呼叫API取得全球確診人數資訊:
```
import requests
import json
url = "https://covid-19-coronavirus-statistics.p.rapidapi.com/v1/total"
querystring = {"country":"Japan"}
headers = {
'x-rapidapi-host': "covid-19-coronavirus-statistics.p.rapidapi.com",
'x-rapidapi-key': "3e99a92190msh101eb7e3faa43a8p13070djsn1178154416e5"
}
response = requests.request("GET", url, headers=headers, params=querystring)
# print(response.text)
data = json.loads(response.text) # 將JSON格式轉成Python資料型態
# 最後要輸出到網頁的內容
result = ''
result += '<!DOCTYPE html>'
result += '<html>'
result += ' <head>'
result += ' <meta chatset"utf-8"/>'
result += ' <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">'
result += '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css" />'
result += ' </head>'
result += ' <body style="margin: 5px" >'
result += '<h1 class="animate__animated animate__bounce">全球武漢肺炎狀況最新資訊</h1>'
result += '<img class="animate__animated animate__slideInLeft" src="covid-19.jpg" width="480px"/><br/><br/>'
result += '<p>'
result += '更新時間:' + data['data']['lastChecked'][:-6].replace('T', ' ') + '<br/>'
result +='確診人數:' + str(data['data']['confirmed']) + '<br/>'
result += '死亡人數:' + str(data['data']['deaths']) + '<br/>'
result += '復原人數:' + str(data['data']['recovered']) + '<br/>'
result += '<iframe width="500" height="250" src="https://www.youtube.com/embed/2fcx4oNPe3Q" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>'
result += '</p>'
result += ' </body>'
result += '</html>'
# print('全球武漢肺炎狀況更新')
# print('更新時間:', data['data']['lastChecked'][:-6].replace('T', ' '))
# print('確診人數:', data['data']['confirmed'])
# print('死亡人數:', data['data']['deaths'])
# print('復原人數:', data['data']['recovered'])
print(result)
with open('covid-19.html', 'w') as src:
src.write(result)
```
> 如果出現ModuleNotFoundError: No module named 'requests'錯誤,則必須先安裝requests模組。
> ```# pip install requests```
>
### 寫入資料到檔案
```
import sys
with open('test.txt', 'w') as src:
src.write('abc你好')
```