6.8. 运算符

numpyndarray 上运行的第一类数学运算是标准的 Python 运算符。算术、比较和按位运算符全都逐元素工作——每个运算符在单次库调用内将数组(或两个数组)从头到尾遍历一次,比等效的 Python for 循环快得多。

6.8.1. 算术运算

+-*///%** 都可以在两个形状兼容的数组之间,或在一个数组与一个标量之间运算:

a = np.array([1, 2, 3, 4], dtype=np.float)
b = np.array([10, 20, 30, 40], dtype=np.float)

print(a + b)        # array([11.0, 22.0, 33.0, 44.0])
print(a * 2)        # array([2.0, 4.0, 6.0, 8.0])
print(b - a)        # array([9.0, 18.0, 27.0, 36.0])
print(b / a)        # array([10.0, 10.0, 10.0, 10.0])

结果的 dtype 遵循 数据类型(Dtypes) 中描述的向上转型规则。整数数组在溢出时会回绕;当这一点重要时,请在运算前先转换为更宽的 dtype。

矩阵乘法运算符 @ 实现。请使用 dot() 进行矩阵/向量乘积运算。

6.8.1.1. 原地形式

每个算术运算符都有一个原地形式——+=-=*=/=%=**=。原地形式会直接写入现有缓冲区,而不是分配一个临时缓冲区:

b = b + 1            # allocates a temporary the size of b
b += 1               # no temporary

在微控制器上,对于任何热循环,第二种形式基本上是必需的。

6.8.2. 按位运算

按位运算符 &|^ 对整数数组逐元素工作。应用于 floatcomplex 数组时会抛出 TypeError:

a = np.array([0b1100, 0b1010], dtype=np.uint8)
b = np.array([0b1010, 0b1100], dtype=np.uint8)
print(a & b)        # array([8, 8], dtype=uint8)
print(a | b)        # array([14, 14], dtype=uint8)
print(a ^ b)        # array([6, 6], dtype=uint8)

一元运算符 ~ 对整数数组执行按位取反(NOT)。

移位运算符 <<>> 在 Python 运算符层面接入。其函数形式 left_shift()right_shift() 可用:

np.left_shift(a, 2)
np.right_shift(b, 1)

6.8.3. 比较运算

==!=<<=>>= 全都返回一个广播形状的 bool ndarray:

a = np.array([1, 2, 3, 4, 5], dtype=np.uint8)
print(a < 3)
# array([True, True, False, False, False], dtype=bool)

这个布尔结果正是 索引选择与重排 所使用的。

6.8.3.1. 左侧规则

在与标量比较时,ndarray 必须位于关系运算符的左侧a > 2 可行;2 < a 会抛出 TypeError。对于对称写法,请使用函数名:

np.greater(5, a)        # 5 > a, element-wise
np.less(5, a)           # 5 < a, element-wise
np.equal(5, a)          # 5 == a, element-wise
np.not_equal(5, a)      # 5 != a, element-wise

6.8.4. 一元运算符

  • +a —— 返回数组的一个副本。

  • -a —— 取负。对于无符号 dtype,值会按模 \(2^N\) 回绕,与二元运算符的方式相同。

  • abs(a) —— 逐元素绝对值。对于无符号 dtype,返回一个副本而不进行计算。

  • ~a —— 按位取反(仅限整数数组)。

  • len(a) —— 返回第一个轴的长度,与 Python 序列约定一致。

6.8.5. 缺少什么

比较运算以及部分按位运算的右侧运算符并未像算术运算符那样实现。当 ndarray 会落在右侧时,请使用函数形式(见上文)。

支持的运算符完整列表及其遵循的向上转型规则,请参见 numpy --- 兼容 numpy 的数组运算