Python 中的神秘運算符

  • 2020 年 1 月 20 日
  • 筆記

今天我們來講講 Python 里一個不為眾人所知的運算符。你可能會覺得疑惑:還有我不知道的運算符?別急着下結論,先往下看看再說。

在 Python3.5 中通過 PEP465

(https://www.python.org/dev/peps/pep-0465)加入了 @運算符,也就是矩陣相乘運算符。雖然目前沒有任何內置的 Python 類型實現了這個運算符的邏輯(就只是挖了個坑),但是如果你用過 numpy,大概對這個運算符的邏輯並不陌生:

>>> a = numpy.array([1, 2, 3])  >>> b = numpy.array([10, 20, 30])  >>> a @ b  140  >>> c = numpy.array([[10, 15], [20, 25], [30, 35]])  >>> d = numpy.array([[4, 5, 6], [7, 8, 9]])  >>> c @ d  array([[145, 170, 195],         [255, 300, 345],         [365, 430, 495]])

如今,在原生的 Python 代碼中,你也可以使用這個運算符。但前提是,你得自己實現具體的運算規則,也就是實現 __matmul__()__rmatmul__() __imatmul__() 這3個方法。

在看實例之前,我們先來了解下這種特殊的類方法。

在官方文檔中,我們看到與 __matmul__ 方法一起介紹的還有 __add____sub__ 等等(注意前後都是2個下劃線),這些方法都是用來定義此類型的運算符號。

假設現在有一個類叫 A,我們在其 class 中實現了加法方法 __add__

def __add__(self, value):      # 具體實現代碼(略)

那麼我們就可以在代碼中對 A 的實例進行加法運算:

a = A()  b = A()  c = a + b

此種情況下,__add__ 函數會被調用,self 對應的是 a 變量,而 value 對應的則是 b 變量。

__matmul__ 與之類似,唯一的不同就是它會在使用 @ 操作符而不是 + 時被調用。

同樣的道理,__rmatmul__ 對應操作數不支持相關運算或者類型不同的情況,__imatmul__ 則對應複合賦值運算符的情況:

a = A()  b = A()  c = a @ b  # __matmul__  d = a @ 1  # __rmatmul__  a @= 1  #__imatmul__

接下來我們來創建一個繼承 list 的類並實現矩陣乘法:

class NewList(list):      def __matmul__(self, v):          result = []          for i in range(len(self)):              result.append([])              for j in range(len(v[0])):                  result[i].append(0)            for i in range(len(self)):              for j in range(len(v[0])):                  for k in range(len(v)):                      result[i][j] += self[i][k] * v[k][j]          return result    # 測試  x = NewList([[7, 7, 3],      [4, 5, 6],      [6, 4, 3]])    y = NewList([[5, 4, 1, 2],      [6, 2, 3, 0],      [4, 5, 6, 1]])    z = x @ y  for i in z:      print(i)

輸出結果:

[89, 57, 46, 17]  [74, 56, 55, 14]  [66, 47, 36, 15]

雖然這個符號的設定是用於矩陣乘法,但實際上可以自定義為任何操作。比如我們可以用它來計算直角坐標繫上兩個點之間的距離

from math import sqrt    class Point:      def __init__(self, x, y):          self.x = x   # x坐標          self.y = y   # y坐標        def __matmul__(self, value):          x_sub = self.x - value.x          y_sub = self.y - value.y          return sqrt(x_sub**2 + y_sub**2)    a = Point(1, 3)  b = Point(4, 7)  print(a @ b)

以上便是我今天跟大家分享的 Python 神秘操作符。

註:本文來自編程教室的讀者 @pynickle 即將發佈的 GitChat 的一部分。此次 GitChat 中,他將會介紹一些 Python 的冷知識(但不適合零基礎小白,明天截止預訂),如果你感興趣的話,歡迎加入一起討論交流。

(點擊文末 閱讀原文 可直達)

作者:pynickle