Learning Objectives
- 比較運算子(Comparison Operators)與串接比較
- 邏輯運算子(Logical Operators)and、or、not 與短路求值
- 身分運算子(Identity Operators)is、is not,以及它和 == 的差別
- 位元運算子(Bitwise Operators)與運算子優先順序
為什麼要認識這些運算子
程式常常要「判斷」:分數及格了嗎?帳號和密碼都對嗎?兩個名稱指的是同一份資料嗎? 這些判斷都靠運算子完成。本課介紹四種運算子:
| 種類 | 運算子 | 運算對象 | 結果 |
|---|---|---|---|
| 比較運算子 | == != < > <= >= |
數值、字串等可以比大小的資料 | True / False |
| 邏輯運算子 | and or not |
真假值 | 決定結果的那個運算元 |
| 身分運算子 | is is not |
任何物件 | True / False |
| 位元運算子 | & | ^ ~ << >> |
整數的二進位位元 | 整數 |
注意
==和=的不同:==是「比較是否相等」,=是「把右邊的值指派給左邊的名稱」。 還有True、False的第一個字母一定大寫;小寫的true會被當成一個(不存在的)變數名稱。
比較運算子與串接比較
每一個比較的結果都是 True 或 False。注意第 8~10 行:Python 允許把比較串在一起,8 > 7 > 6 相當於 8 > 7 and 7 > 6,中間的 7 只算一次。
邏輯運算子:and、or、not
Python 的邏輯運算子用英文單字,不用符號(&、| 是另一種:位元運算子)。
| 運算 | 規則 | 白話 |
|---|---|---|
x and y |
x 是假值就回傳 x,否則回傳 y | 兩個都要真 |
x or y |
x 是真值就回傳 x,否則回傳 y | 至少一個真 |
not x |
x 是假值就回傳 True,否則 False |
真假顛倒 |
假值有:False、None、0、0.0、空字串 ""、空串列 []、空元組、空字典、空集合;其他都是真值。
三個重點:
1. 短路求值:and 的左邊已經是假,結果一定是假;or 的左邊已經是真,結果一定是真。這時右邊根本不會被計算。
2. and、or 回傳的不一定是 True/False,而是「決定結果的那個運算元」。所以 0 or 9 是 9。
3. not 的優先順序比比較運算子低:not a == b 會被解讀成 not (a == b),而 a == not b 是語法錯誤。
and / or 的回傳值與短路求值
先看第 3~5 行的一般用法,再看第 8~10 行:and、or 印出來的是 0 或 9,不是 True/False。最後兩段示範短路求值的實際用途:避免除以零、提供預設值。
想一想:比較與邏輯
下面的程式會印出什麼?
print(3 < 5 > 4)
下面的程式會印出什麼?
print(0 or "" or "hi")
下面的程式會印出什麼?
print(2 and 0 and 3)
身分運算子:is 與 ==
==比較的是內容:兩邊的值相不相等。is比較的是身分:兩個名稱是不是指向同一個物件(也就是id(x) == id(y))。
兩個內容一模一樣的串列,== 是 True,is 卻可能是 False,因為它們是兩個不同的物件。
這個差別在 Module 09 串列的「別名」會非常重要。
投影片示範
x = 5; y = 5; x is y得到True,那是因為 Python 會讓小整數共用同一個物件,屬於實作上的細節。 比較數值、字串請一律用==;is留給「是不是同一個物件」以及x is None這種判斷。
is 與 == 的差別
看右邊的物件圖:a 和 b 各自指向一個串列,c = a 之後,c 和 a 的箭頭指向同一個串列。第 11 行透過 c 修改,再看 a 有沒有跟著變。
位元運算子:一個位元一個位元算
位元運算把整數寫成二進位,每一個位元各自運算:
| p | q | p & q(AND) | p | q(OR) | p ^ q(XOR) |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
另外還有 ~x(每個位元反轉,結果等於 -(x + 1))、x << n(左移 n 位,等於乘以 2 的 n 次方)、
x >> n(右移 n 位,等於除以 2 的 n 次方後取整數)。
用二進位看位元運算
輸出區把每個結果都用 8 位數的二進位印出來,把 a 和 b 的位元上下對齊,逐欄套用上面的真值表。
運算子優先順序
同一行有好幾個運算子時,優先順序高的先算。由高到低大致是:括號 → ** → 正負號 → * / // % → + - → 位元運算 → 比較、in、is → not → and → or。
想一想:身分、位元與優先順序
下面的程式會印出什麼?
a = [1, 2]
b = [1, 2]
print(a == b, a is b)
下面的程式會印出什麼?
print(6 & 3, 6 | 3)
下面的程式會印出什麼?
print(2 + 3 * 2 ** 2)
實作:閏年判斷
閏年的規則:能被 4 整除但不能被 100 整除,或者能被 400 整除。把三個 TODO 底下的 False 換成同一個判斷式(只用 %、==、!=、and、or),讓程式印出正確結果。
重點精華回顧
- 比較運算子針對數值或變數的值做比較,結果是
True/False;可以串接,a < b <= c相當於a < b and b <= c。 - 邏輯運算子
and、or、not的對象是真假值;有短路求值,而且and、or回傳的是決定結果的那個運算元。 - 身分運算子
is、is not比較兩個名稱是否指向同一個物件;比較內容請用==,判斷None用is。 - 位元運算子
&、|、^、~、<<、>>針對二進位的每一個位元運算。 - 優先順序:
**→ 算術 → 位元 → 比較 →not→and→or;不確定時就加括號,也讓程式更好讀。