M3 值與型別
- 認出
int、float、str、bool、None五種值,並用type()查出一個值的型別; - 說出
"120"和120的分別,以及各自能做甚麼運算; - 用
int()、float()、str()轉換型別,並看懂轉換失敗時的錯誤訊息; - 理解變數只是貼在值上的名字。
1每個值都有型別 Every value has a type
M0 那本書的資料,讀入 Python 之後,每一個值都有自己的型別(type):
用 type() 可以查出一個值的型別:
type(120) # <class 'int'>
type(120.0) # <class 'float'>
type("120") # <class 'str'>
type(True) # <class 'bool'>
type(None) # <class 'NoneType'>
這一單元講這五種最基本的值。list 和 dict 這兩種容器留待 M4。
| 型別 | 中文 | 例子 | JSON 的寫法 |
|---|---|---|---|
int |
整數 | 120、-3、0 |
120 |
float |
浮點數(有小數) | 21.5、0.8、120.0 |
21.5 |
str |
字串(文字) | "看得見的資料"、"120" |
"120" |
bool |
布林值(是/否) | True、False |
true、false |
NoneType |
沒有值 | None |
null |
2數字:int 和 float Numbers: int and float
數字有兩種:沒有小數點的是 int,有小數點的是 float。120 和 120.0 數值相等,型別卻不同。
| 運算 | 例子 | 結果 | 說明 |
|---|---|---|---|
+ - * |
120 * 3 |
360 |
加減乘 |
/ |
7 / 2、6 / 2 |
3.5、3.0 |
除法,結果一定是 float |
// |
7 // 2 |
3 |
整除,捨去小數 |
% |
7 % 2 |
1 |
餘數 |
** |
2 ** 10 |
1024 |
次方 |
0.1 + 0.2 # 0.30000000000000004
0.1 + 0.2 == 0.3 # False
電腦用二進制儲存 float,大部分小數只能儲存一個非常接近的值。計算價錢時,這點誤差通常無關痛癢;但不要用 == 比較兩個 float。工程線的練習會處理這個問題。
3字串 Strings
字串就是文字,用引號包住。單引號和雙引號都可以,本課統一用雙引號。
title = "看得見的資料"
len(title) # 6:字串有多少個字
"價錢:" + str(120) # '價錢:120':+ 把兩個字串接起來
要把數值放進一句文字裏,最方便的是 f-string:在引號前面加 f,把要放進去的值寫在 { } 裏。
price = 120
f"打八折後是 {price * 0.8} 元" # '打八折後是 96.0 元'
最容易出錯的,是看起來像數字的字串:
"120" + "1" # '1201':兩個字串接起來
120 + 1 # 121:兩個數字相加
"120" + 1 # TypeError: can only concatenate str (not "int") to str
"120" 有引號,是文字,不是數字。從表單、CSV 檔或網頁讀回來的資料,數字常常以字串的形式出現,要先轉換才能計算(見第 5 節)。
4布林值和 None Booleans and None
布林值只有兩個:True 和 False,注意首字母大寫。比較的結果就是布林值:
3 > 2 # True
"apple" == "Apple" # False:大小寫不同就是不同的字串
stock = 0
stock > 0 # False:沒有現貨
= 和 == 不同:= 是把值交給一個名字,== 是比較兩個值是否相等。
None 表示「沒有值」。M0 那本書的作者電郵在 JSON 裏寫作 null,讀入 Python 就變成 None。它和 0、""(空字串)都不同:0 是一個數字,"" 是一段沒有字的文字,None 是根本沒有填。
email = None
email is None # True:檢查是否 None,用 is
None == 0 # False
5型別轉換 Converting between types
用型別的名字當作函式,就可以把值轉換成那個型別:
flowchart LR
S["str<br/>#quot;120#quot;"] -->|"int()"| I["int<br/>120"]
S -->|"float()"| F["float<br/>120.0"]
I -->|"str()"| S
F -->|"int():捨去小數"| I
I -->|"float()"| F
X["str<br/>#quot;12.5#quot;"] -.->|"int() 失敗"| E["ValueError"]
classDef v fill:#ffffff,stroke:#d3d1cb,color:#37352f
classDef err fill:#fdebec,stroke:#c4423d,color:#5d1715
class S,I,F,X v
class E err
int("120") # 120
float("12.5") # 12.5
str(12.5) # '12.5'
int(12.9) # 12:直接捨去小數,不是四捨五入
round(12.9) # 13:要四捨五入用 round()
轉換不了的時候,Python 會報 ValueError,並在訊息裏引出那個值:
int("12.5") → ValueError: invalid literal for int() with base 10: '12.5'
讀錯誤訊息時,先看最後一行:錯誤的種類(ValueError),以及出事的值('12.5')。這裏的意思是:"12.5" 有小數點,不能直接變成整數,要先用 float()。
bool() 不會讀懂字串的意思,它只看「有沒有東西」:空的或零值是 False,例如空字串 ""、0、0.0、None(還有 M4 會講的空 list []、空 dict {});其餘都是 True,包括字串 "False" 和 "0"。從檔案讀到 "false"、"no" 這類文字,要自己比較:value == "true"。
6變數只是名字 A variable is just a name
price = 120 不是把 120 放進一個叫 price 的盒子裏,而是把 price 這個名字貼在 120 這個值上。同一個值可以貼上幾個名字;把名字改貼到別的值上,原來的值不受影響。
price = 120
old_price = price # 兩個名字指向同一個 120
price = 150 # price 改指向 150
old_price # 仍然是 120
這個「名字指向值」的看法,在 M4 處理 list 和 dict 時會變得很重要。
7容易混淆 Easily confused
| 這兩樣 | 分別 |
|---|---|
"120" 和 120 |
前者是字串,+ 會把它接起來;後者是數字,+ 會相加。 |
None、0、"" |
沒有填、填了零、填了空白文字。三者互不相等。 |
= 和 == |
前者把值交給一個名字;後者比較兩個值是否相等。 |
/ 和 // |
前者是除法,結果一定是 float;後者是整除,捨去小數。 |
int(12.9) 和 round(12.9) |
前者捨去小數得 12;後者四捨五入得 13。 |
JSON 的 true/null 和 Python 的 True/None |
寫法不同,意思相同;讀入時自動轉換。 |
8動手:新書到貨 Hands-on: new arrivals
- 下載
m3-new-arrivals.ipynb和new-arrivals.json,放進python-course資料夾; - 執行
uv run jupyter lab,打開 notebook,逐格執行。
小書店收到一批新書的資料,是從網上表單匯出的:有些價錢是字串,有些庫存沒有填。你要先查出每個值的型別,找出會令計算出錯的值,再把它們轉換好,算出這批書的總值。
m3-sensor-units-eng.ipynb:浮點數誤差和 math.isclose、攝氏轉換成開爾文和華氏,以及用 None 表示缺失的讀數、計算平均值時把它跳過。
M4 講容器:用 list 和 dict 把很多個值組織起來,也就是 JSON 和 YAML 讀入後的樣子。