从 JavaScript 角度学 Python(12) - 运算子

前言

运算子是一个非常常用的方法,因此在基础观念中也是绝对闪不了的。

运算子

最基本的运算子不外乎就是 + (加)、- (减)、* (乘)、/ (除) 这几个运算子,而这几个运算子又称之为算数运算子,在 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 的除法竟然输出一个浮点数

https://ithelp.ithome.com.tw/upload/images/20210912/20119486hotYL5usCi.png

如果你写过 Python2 的人可能会更 WT...

所以这边先让我插入一个小片段说明一下 Python2 与 Python3 之间的差异。

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」官方文件。

好吧,这时候你应该会想说「如果我就是想要整数除法的话,该怎麽做呢?」

所以就让我们继续接着看下去吧~

https://ithelp.ithome.com.tw/upload/images/20210912/20119486gLO9voIzHr.png

// 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

https://ithelp.ithome.com.tw/upload/images/20210912/20119486j5sKx9TSql.png

简单来讲 // 具备与 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 文件 中有讲到这句话

请注意比较、成员检测和标识号检测均为相同优先级,并具有如 比较运算 一节所描述的从左至右串连特性。

因此你也可以在上面连结中看到底下这一张图,最上方优先性最高,最下方则是优先性最低:

https://ithelp.ithome.com.tw/upload/images/20210912/20119486vDl5hsXu2e.png

所以前面的范例 print(3 > 2 > 1) # True 范例程序码它到底是怎麽运作的呢?

我们可以依照官方文件的解释来推敲一二:

例如 x < y <= z 等价于 x < y and y <= z,除了 y 只被求值一次(但在两种写法下当 x < y 值为假时 z 都不会被求值)。

透过上面的解释,我们可以得知范例程序码在运作的模式其实是 3 > 22 > 1,这边要注意中间是 & 符号,代表着两边若有一边是 False 就会整个回传 False,例如:

print(3 > 2 > 4) # False

如果用比较白话一点方式来看的话,结果就会像是 3 > 2 & 2 > 4

那我们今天运算子的章节就先到这边结束罗~

参考文献

作者的话

上网找到了一篇「绝对不会失败的溏心蛋」,结果实作之後还是失败了 Orz,实在太过不熟...但是不得不说有机鸡蛋的蛋腥味真的还好呢!

关於兔兔们

兔法无边


<<:  【在 iOS 开发路上的大小事-Day01】先装个 Xcode 开发环境压压惊

>>:  Day 13 : 弱监督式标注资料 Snorkel (视觉关系侦测篇)

[Day 10] 简单的单元测试实作(四)-关於程序的问题,一律建议重构

为了要让程序码更简洁、更容易懂、及更容易维护, 我们今天要开始将之前的测试程序重构,(虽然好像才刚开...

从 IT 技术面细说 Search Console 的 27 组数字 KPI (4) 流量:图片、影片、新闻等多媒体流量

『导言』在 2019 年後,多媒体的搜寻流量在一些网站占比越来越高,现在可以说是经营网站的人都要去注...

Event Correlation 事件相关性

通常分析 Event Correlation事件相关性,商业分析师 BA(Business Anal...

涂鸦冠军岛 - Google 专为东京奥运打造的线上小游戏~

打开 Google Chrome, 首页就可以看到罗~ ...

Episode 4 - 变数及型态

如果画面太小或看不清楚,可移驾至 https://www.youtube.com/watch?v=...