從 JavaScript 角度學 Python(14) - BMI 計算(2)

前言

前面章節也算是已經告一個段落了,所以接下來一樣來簡單實作一下前面的小知識點。

BMI 章節回顧

在第六天的結尾處我們有寫了一段簡單的 BMI 計算功能:

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
def bmi (kg, cm):
if kg <= 0:
return print('體重輸入不正確')
if cm <= 0:
return print('身高輸入不正確')

bmiNum = round((kg / ((cm ** 2) / 100)) * 100, 2)

if bmiNum < 18.5:
print('你目前的 BMI 是:', bmiNum, '介於過輕範圍內。')
elif 18.5 >= bmiNum < 24:
print('你目前的 BMI 是:', bmiNum, '介於正常範圍內。')
elif 24 >= bmiNum < 27:
print('你目前的 BMI 是:', bmiNum, '介於過重範圍內。')
elif 27 >= bmiNum < 30:
print('你目前的 BMI 是:', bmiNum, '介於輕度範圍內。')
elif 30 >= bmiNum < 35:
print('你目前的 BMI 是:', bmiNum, '介於中度範圍內。')
elif bmiNum >= 35:
print('你目前的 BMI 是:', bmiNum, '介於重度範圍內。')
else:
print('計算錯誤。')

print(bmi(68, 175)) # 你目前的 BMI 是: 22.2 介於過重範圍內。
print(bmi(95, 185)) # 你目前的 BMI 是: 27.76 介於中度範圍內。

接下來我們會將上面的程式碼加入一些小功能

小功能

(上面這張圖真好用。)

所以這邊我們先來聊一下使用者故事吧。

BMI 使用者故事

  • 使用者可以透過終端機輸入身高與體重,而必須是身高優先輸入接下來才是體重。
  • 輸出結果之前會先出現 Loading 讀取中約五秒。
  • 五秒之後會輸出結果顯示 BMI 值與目前 BMI 介於哪個範圍內。
    • 例如:你目前的 BMI 是: 22.2 介於過重範圍內。

最後在列一個進階一點的東西好了

  • 輸出的結果必須依據以下範圍輸出相關的文字顏色
    • 例如:「你目前的 BMI 是: 22.2 介於過重範圍內」的這句話的過重必須是橘色。
    • 過輕 - 黃色
    • 正常 - 綠色
    • 輕度 - 黃色
    • 中度 - 橘色
    • 重度 - 紅色

那麼看完上方使用者故事之後就準備來實作吧。

往下看之前你也可以先試著自己製作看看,如果沒有想法的話,再往下參考我的作法哩。

使用者自定義輸入身高與體重

剛好這邊的技巧在前一個章節已經有講到,想必記憶非常深刻,所以我們這邊會需要宣告身高與體重這兩個變數,但是這邊要注意使用者故事有講到一件事情:

「使用者可以透過終端機輸入身高與體重,而必須是身高優先輸入接下來才是體重。」

因此這邊我們可以知道我們將會使用 input 函式,接下來另一個問題是,使用者所輸入的值都會是字串,因此這邊應該使用正整數?還是浮點數呢?通常來講我們在量身高與體重的時候都會有小數點,因此這邊會建議使用 float() 包覆 input() 函式:

1
2
cm = float(input('請輸入您的身高:'))
kg = float(input('請輸入您的體重:'))

那麼第一條使用者故事這邊算是完成了。

Loading 效果

接下來是關於終端機上呈現 Loading 的效果,這一個效果在使用者故事上有講到要等五秒

「輸出結果之前會先出現 Loading 讀取中約五秒。」

這個功能的關鍵重點技術在於 for loopprint,因此可能會需要跑五次迴圈:

1
2
for i in range(5):
print('Loading')

基本上上面的程式碼結果就會是跑五次 Loading,但是可能有點不好看,所以我們可以稍微調整一下變成 Loading.....,那麼因此要做到這個效果我們就必須使用 printend 參數,這是什麼意思呢?我們可以看到是五個點點,因此我們可以先將 Loading 文字輸出,接下來的點點點都是用迴圈輸出,那麼預設情況下 print\n 換行,因此我們要改成無換行模式:

1
2
3
print('Loading', end='')
for i in range(5):
print('.', end='')

上面的結果基本上已經非常接近我們要的效果 Loading.....,接下來就是我們要如何做到點點點是一秒一個點的方式,那麼由於 Python 內建是沒有時間相關處理的函式,因此這邊就必須額外引入 Python 的 time 模組(import time),而 time 底下有一個 sleep 函式,這個函式主要用途是讓程式碼暫停你要停止的秒數,所以這一段 sleep 補在 print('.', end='') 結尾處就可以了:

1
2
3
4
5
6
import time

print('Loading', end='')
for i in range(5):
print('.', end='')
time.sleep(1) # 讓程式碼睡一秒

這時候你應該會發現程式碼沒有即時地出現,當它出現的時候已經是一個完整的 Loading..... 而不是一個點點點出現,這邊問題主要是出在 print 參數中有一個 flush 導致的,預設 flushFalse,而這個參數會先將預期要輸出的結果放到記憶體中等待迴圈全部跑完再一次輸出,如果改完 True 的話就會變成不讓它將結果放到記憶體中而是直接輸出:

1
2
3
4
5
6
import time

print('Loading', end='')
for i in range(5):
print('.', end='', flush=True)
time.sleep(1) # 讓程式碼睡一秒

所以 flush 其實是一個很特別的屬性唷~

輸出 BMI 結果

這邊先整理一下程式碼,先將上面 inputLoading 程式碼都整合之後再來看一下:

1
2
3
4
5
6
7
8
9
import time

cm = float(input('請輸入您的身高:'))
kg = float(input('請輸入您的體重:'))

print('Loading', end='')
for i in range(5):
print('.', end='', flush=True)
time.sleep(1) # 讓程式碼睡一秒

那麼其實 BMI 計算這邊我就偷懶一下,直接將先前的寫好的計算程式碼直接拉進來微調就好了:

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
import time

cm = float(input('請輸入您的身高:'))
kg = float(input('請輸入您的體重:'))

def bmi (kg, cm):
if kg <= 0:
return print('體重輸入不正確')
if cm <= 0:
return print('身高輸入不正確')

bmiNum = round((kg / ((cm ** 2) / 100)) * 100, 2)

if bmiNum < 18.5:
print('你目前的 BMI 是:', bmiNum, '介於過輕範圍內。')
elif 18.5 >= bmiNum < 24:
print('你目前的 BMI 是:', bmiNum, '介於正常範圍內。')
elif 24 >= bmiNum < 27:
print('你目前的 BMI 是:', bmiNum, '介於過重範圍內。')
elif 27 >= bmiNum < 30:
print('你目前的 BMI 是:', bmiNum, '介於輕度範圍內。')
elif 30 >= bmiNum < 35:
print('你目前的 BMI 是:', bmiNum, '介於中度範圍內。')
elif bmiNum >= 35:
print('你目前的 BMI 是:', bmiNum, '介於重度範圍內。')
else:
print('計算錯誤。')

print('Loading', end='')
for i in range(5):
print('.', end='', flush=True)
time.sleep(1)

bmi(kg, cm)

這邊基本上程式碼就已經完成八成了,可是你如果嘗試輸入以上程式碼時,會發現 Loading 會與輸出結果混在一起,所以這邊就要調整一下 for loop 的內容,只要迴圈跑到最後一次時,就要變成原本的 \n 換行輸出:

1
2
3
4
5
6
7
print('Loading', end='')
for i in range(5):
if i < 4:
print('.', end='', flush=True)
else:
print('.', flush=True)
time.sleep(1)

這樣子輸出的結果就會正常囉~

文字顏色

其實關於文字顏色的部分相對是比較困難的,剛好在 stack overflow 剛好就有人提出來:

1
2
3
4
5
6
7
8
9
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'

因此我們可以整理成自己的字典來使用:

1
2
3
4
5
6
color = {
'OKGREEN': '\033[92m',
'WARNING': '\033[93m',
'FAIL': '\033[91m',
'ENDC': '\033[0m',
}

接下來使用方式非常簡單,這邊就讓我用「字串格式化(String formatting)」來重新調整文字顏色:

1
print(f'你目前的 BMI 是:{bmiNum}介於{color["WARNING"]}過輕{color["ENDC"]}範圍內。')

請注意!字典取值的地方要改成雙引號,否則會出現 SyntaxError: f-string: unmatched '[' 的錯誤訊息唷。

那麼這樣子就完成了比較進階的 BMI 計算功能,最後這邊也提供完整的範例給予參考:

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
import time

color = {
'OKGREEN': '\033[92m',
'WARNING': '\033[93m',
'FAIL': '\033[91m',
'ENDC': '\033[0m',
}

cm = float(input('請輸入您的身高:'))
kg = float(input('請輸入您的體重:'))

def bmi (kg, cm):
if kg <= 0:
return print('體重輸入不正確')
if cm <= 0:
return print('身高輸入不正確')

bmiNum = round((kg / ((cm ** 2) / 100)) * 100, 2)

if bmiNum < 18.5:
print(f'你目前的 BMI 是:{bmiNum}介於{color["WARNING"]}過輕{color["ENDC"]}範圍內。')
elif 18.5 >= bmiNum < 24:
print(f'你目前的 BMI 是:{bmiNum}介於{color["OKGREEN"]}正常{color["ENDC"]}範圍內。')
elif 24 >= bmiNum < 27:
print(f'你目前的 BMI 是:{bmiNum}介於{color["WARNING"]}過重{color["ENDC"]}範圍內。')
elif 27 >= bmiNum < 30:
print(f'你目前的 BMI 是:{bmiNum}介於{color["WARNING"]}輕度{color["ENDC"]}範圍內。')
elif 30 >= bmiNum < 35:
print(f'你目前的 BMI 是:{bmiNum}介於{color["FAIL"]}中度{color["FAIL"]}範圍內。')
elif bmiNum >= 35:
print(f'你目前的 BMI 是:{bmiNum}介於{color["FAIL"]}重度{color["FAIL"]}範圍內。')
else:
print('計算錯誤。')

print('Loading', end='')
for i in range(5):
if i < 4:
print('.', end='', flush=True)
else:
print('.', flush=True)
time.sleep(1)
bmi(kg, cm)

今天這一份程式碼將會放在這個儲存庫:https://github.com/hsiangfeng/javascript-to-python

參考文獻

作者的話

紹興醉雞的口感跟花雕雞差不多,但是這次似乎沒有將紹興酒的酒味給逼出來有點可惜。

關於兔兔們

兔法無邊

Liker 讚賞

這篇文章如果對你有幫助,你可以花 30 秒登入 LikeCoin 並點擊下方拍手按鈕(最多五下)免費支持與牡蠣鼓勵我。
或者你可以也可以請我「喝一杯咖啡(Donate)」。

Buy Me A Coffee Buy Me A Coffee

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ