運算子是一個非常常用的方法,因此在基礎觀念中也是絕對閃不了的。
最基本的運算子不外乎就是 +
(加)、-
(減)、*
(乘)、/
(除) 這幾個運算子,而這幾個運算子又稱之為算數運算子,在 JavaScript 中的寫法也不會與 Python 有太大的差異。
JavaScript:
console.log(1 + 1); // 2
console.log(2 * 2); // 4
console.log(8 - 2); // 6
console.log(16 / 2); // 8
Python:
print(1 + 1) # 2
print(2 * 2) # 4
print(8 - 2) # 6
print(16 / 2) # 8.0
而上面又稱之為四則運算,但是唯獨除法你可以看到 Python 是輸出一個浮點數(float)。
疑?你注意到了嗎?Python 的除法竟然輸出一個浮點數
如果你寫過 Python2 的人可能會更 WT...
所以這邊先讓我插入一個小片段說明一下 Python2 與 Python3 之間的差異。
這是一個比較特別的的地方,在原本的 Python2 的除法會是一個整數:
# Python2
print(16 / 2) # 8
雖然看起來沒有什麼太大問題,但是如果你要做較精準的系統開發時,其實這就會有問題,因為 Python2 的除法會無條件捨去小數點,因此 Python2 的又稱之為 floor division 或 integer division(整數除法?應該是這樣翻,有錯的話再跟我說一下)。
那就如同前面所言,如果我們要做比較精準處理時,就會有一些問題:
# Python2
print(16 / 3 == 16.0 / 3.0) # False
嚇到了嗎?如果沒有嚇到的話,我倒是嚇到了,畢竟這兩者應該是相同的才對,這個原因是因為 Python2 的除法運算子本身具有兩種能力,也就是 Floating point division 與 integer division 的能力,只要你針對數字加上小數點就會被轉換成浮點數除法。
那麼為了解決這個問題,Python3 之後將 /
運算子只保留 Floating point division 的能力也就是浮點數除法,所以在 Python3 結果就會變成以下:
# Python3
print(16 / 3) # 5.333333333333333
那麼因為這個修正的關係,我們在做較精準的計算上就可以比較安全:
# Python3
print(16 / 3 == 16.0 / 3.0) # True
如果你想更深入了解的話,建議你可以閱讀這一篇 「PEP 238 -- Changing the Division Operator」官方文件。
好吧,這時候你應該會想說「如果我就是想要整數除法的話,該怎麼做呢?」
所以就讓我們繼續接著看下去吧~
由於 Python3 的修正關係,因此如果你依然想要做到整數除法的話,你可以使用 //
運算子:
print(16 // 3) # 5
雖然 //
運算子可以做到原本 Python2 類似的整數除法效果:
print(16 // 2) # 8
print(type(16 // 2)) # int
但是這邊問題來了,你可以試著思考一下以下程式碼結果為何:
print(100 / 3, type(100 / 3)) # ?, ?
print(100 // 3, type(100 // 3)) # ?, ?
print(100. // 3, type(100. // 3)) # ?, ?
緊張刺激了嗎?讓我們來看看結果吧!
print(100 / 3, type(100 / 3)) # 33.333333333333336, float
print(100 // 3, type(100 // 3)) # 33, int
print(100. // 3, type(100. // 3)) # 33.0, float
簡單來講 //
具備與 Python2 相同的能力,同時具有整數除法與浮點數除法的能力,所以在使用上就要多加小心。
這邊也額外提一下關於加號運算子的部分,加號運算子其實滿特別的。
為什麼這樣講呢?舉例來講如果你想把兩個陣列合併的話在 JavaScript 中最簡單的就是使用 concat()
函式:
var a = [1, 2, 3];
var b = [4, 5, 6];
var c = a.concat(b);
console.log(c); // [ 1, 2, 3, 4, 5, 6 ]
另一種比較進階的作法則是使用 ES6 展開運算子:
var a = [1, 2, 3];
var b = [4, 5, 6];
var c = [...a, ...b];
console.log(c); // [ 1, 2, 3, 4, 5, 6 ]
如果你用加號運算子就會是另一種結果:
var a = [1, 2, 3];
var b = [4, 5, 6];
var c = a + b;
console.log(c); // "1,2,34,5,6"
至於原因的話,可以詳見我先前寫的 筆記 有提到此觀念,所以這邊就不多述了。
那麼 Python 呢?Python 就比較特別一點,Python 可以使用 加號運算子 快速達到這個陣列合併這個需求:
a = [1, 2 ,3]
b = [4, 5, 6]
c = a + b
print(c) # [1, 2, 3, 4, 5, 6]
超簡單的對吧!
這邊也簡單提一下另一種合併陣列的方式,也就是前一章節所提到的 extend()
,但是這邊要注意 extend()
會修改原有的陣列,因此並不會回傳結果到新的變數上,這一點一定要注意一下:
a = [1, 2 ,3]
b = [4, 5, 6]
c = a.extend(b)
print(c) # None
print(a) # [1, 2, 3, 4, 5, 6]
最後這邊還有另一種更簡短加號運算子寫法,但是它與 extend
的效果是一樣的,它是修改原始的串列在賦予回去:
哦對了!Python 還有一些好玩的地方,例如你可以使用乘法運算子讓字串加倍:
print('Ray' * 4) # RayRayRayRay
甚至是串列與元組的加倍也可以:
print(('https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/') * 4) # ('https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/', 'https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/', 'https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/', 'https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/')
# 元組
print(['https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/'] * 4) # ['https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/', 'https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/', 'https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/', 'https://www.facebook.com/HsiangFengWeb', 'https://hsiangfeng.github.io/']
那麼在四則運算中,其實還有一些比較進階一點的用法,例如次方:
print(10 ** 2) # 100
print(12 ** 2) # 144
其他還有常見的運算子,例如:大於(>
)、小於(<
)、大於等於(>=
)、小於等於(<=
)等等,這邊就只是列出四則運算多部份而已。
最後我想聊一個在 JavaScript 面試考題很常出現的問題,在 JavaScript 我們知道有所謂的優先性與相依性的觀念,如果你不清楚的話,我會建議可以參考這一篇 JavaScript 核心觀念(16)-運算子、型別與文法-優先性及相依性 文章。
那麼以 JavaScript 中很經典的題目就是 console.log(1 < 2 < 3); // true
但是反之若變成了 console.log(3 > 2 > 1); // false
結果就不同了。
所以這邊也想探討看看 Python 也會有這個問題嗎?其實我們可以實際試試看:
print(1 < 2 < 3) # True
print(3 > 2 > 1) # True
可以看到結果都是 True
,這樣代表 Python 沒有優先性與相依性的問題嗎?
其實是有的。
在 Python 文件 中有講到這句話
请注意比较、成员检测和标识号检测均为相同优先级,并具有如 比较运算 一节所描述的从左至右串连特性。
因此你也可以在上面連結中看到底下這一張圖,最上方優先性最高,最下方則是優先性最低:
所以前面的範例 print(3 > 2 > 1) # True
範例程式碼它到底是怎麼運作的呢?
我們可以依照官方文件的解釋來推敲一二:
例如 x < y <= z 等价于 x < y and y <= z,除了 y 只被求值一次(但在两种写法下当 x < y 值为假时 z 都不会被求值)。
透過上面的解釋,我們可以得知範例程式碼在運作的模式其實是 3 > 2
與 2 > 1
,這邊要注意中間是 &
符號,代表著兩邊若有一邊是 False
就會整個回傳 False
,例如:
print(3 > 2 > 4) # False
如果用比較白話一點方式來看的話,結果就會像是 3 > 2 & 2 > 4
。
那我們今天運算子的章節就先到這邊結束囉~
上網找到了一篇「絕對不會失敗的溏心蛋」,結果實作之後還是失敗了 Orz,實在太過不熟...但是不得不說有機雞蛋的蛋腥味真的還好呢!