使用者:Xyy23330121/Python/類的方法與數學運算
在 Python 中,數學運算是用方法來實現的。方法的返回值就是數學運算的結果。以下將講解各個數學運算所對應的方法。
一元運算[編輯 | 編輯原始碼]
基本一元運算[編輯 | 編輯原始碼]
以下是一元運算的列表:
名稱 | 運算符 函數 |
對應方法 |
---|---|---|
負 | -a |
a.__neg__(self)
|
正 | +a |
a.__pos__(self)
|
絕對值 | abs(a) |
a.__abs__(self)
|
按位 取反 |
~a |
a.__invert__(self)
|
這些方法的返回值,應當是讀者希望一元運算返回的結果。
特殊一元運算[編輯 | 編輯原始碼]
除上面的運算之外,還有一些一元運算需要特殊備註:
名稱 | 運算符 函數 |
對應方法 |
---|---|---|
捨入 | round(a) |
a.__round__(self[, ndigits])
|
向零 取整 |
math.trunc(a) |
a.__trunc__(self)
|
向下 取整 |
math.floor(a) |
a.__floor__(self)
|
向上 取整 |
math.ceil(a) |
a.__ceil__(self)
|
round(number, ndigits=None)
支持第二個參數,因此對應的 __round__
方法也應當支持可選的第二個參數。
其它三個方法 math.trunc(x)
、math.floor(x)
和 math.ceil(x)
都是 math
模組中的函數。在使用時,需要先 import math
。
二元運算[編輯 | 編輯原始碼]
我們將詳細講解加法運算,然後直接類推到其它種類的運算。
加法[編輯 | 編輯原始碼]
我們有三個方法與加法運算有關。
object.__add__(self, other)[編輯 | 編輯原始碼]
此方法對應 self + other
的情況。該方法返回的值應當為程式設計師希望 self + other
所得到的結果。
object.__radd__(self, other)[編輯 | 編輯原始碼]
此方法對應 other + self
的情況。該方法返回的值應當為程式設計師希望 other + self
所得到的結果。
object.__iadd__(self, other)[編輯 | 編輯原始碼]
此方法對應 self += other
的情況。該方法應該直接修改self
自身,並返回修改的結果。而非僅返回一個結果。
這並不代表返回的修改結果必須和修改後的self
相等。
調用順序[編輯 | 編輯原始碼]
對於 a + b
運算時,會先嘗試調用 a.__add__
方法。如果調用失敗,且 a
與 b
的類型不相同時,則會嘗試調用 b.__radd__
。
而對於 a += b
運算時,會先嘗試調用 a.__iadd__
方法。如果調用失敗,就改為計算 a = a + b
,並按照上面規則嘗試調用 a.__add__
或 b.__radd__
。
所以,只要良好地定義了 __add__
方法,就可以實現任何類型的加法運算。
其它二元運算[編輯 | 編輯原始碼]
其它二元運算和加法完全是類似的。我們將這些運算總結到以下列表里。
名稱 | 運算符 函數 |
對應方法 | 對應方法(右) | 賦值運算 |
---|---|---|---|---|
加法 | a+b |
a.__add__(self, other) |
b.__radd__(self, other) |
a.__iadd__(self, other)
|
減法 | a-b |
a.__sub__(self, other) |
b.__rsub__(self, other) |
a.__isub__(self, other)
|
乘法 | a*b |
a.__mul__(self, other) |
b.__rmul__(self, other) |
a.__imul__(self, other)
|
矩陣乘法 | a@b |
a.__matmul__(self, other) |
b.__rmatmul__(self, other) |
a.__imatmul__(self, other)
|
除法 | a/b |
a.__truediv__(self, other) |
b.__rtruediv__(self, other) |
a.__itruediv__(self, other)
|
帶餘除法求商 | a//b |
a.__floordiv__(self, other) |
b.__rfloordiv__(self, other) |
a.__ifloordiv__(self, other)
|
求模 | a%b |
a.__mod__(self, other) |
b.__rmod__(self, other) |
a.__imod__(self, other)
|
帶餘除法 | divmod(a,b) |
a.__divmod__(self, other) |
b.__rdivmod__(self, other) |
|
冪 | a**b pow(a,b) |
a.__pow__(self, other[, modulo]) |
b.__rpow__(self, other[, modulo]) |
a.__ipow__(self, other[, modulo])
|
按位左移 | a<<b |
a.__lshift__(self, other) |
b.__rlshift__(self, other) |
a.__ilshift__(self, other)
|
按位右移 | a>>b |
b.__rshift__(self, other) |
b.__rrshift__(self, other) |
a.__irshift__(self, other)
|
按位與 | a&b |
a.__and__(self, other) |
b.__rand__(self, other) |
a.__iand__(self, other)
|
按位異或 | a^b |
a.__xor__(self, other) |
b.__rxor__(self, other) |
a.__ixor__(self, other)
|
按位或 | a|b |
a.__or__(self, other) |
b.__ror__(self, other) |
a.__ior__(self, other)
|
其中,特殊的:
冪運算[編輯 | 編輯原始碼]
函數pow(base, exp, mod=None)
支持第三個數值[1]。所以如果要用__pow__
方法來支持傳入三個參數的pow()
函數,則__pow__
方法應當支持可選的第三個參數。
由於強制轉換規則會太過複雜,三元版的pow()
不會嘗試調用__rpow__
。
矩陣乘法[編輯 | 編輯原始碼]
Python 內置了矩陣乘法的運算符 @
,但 Python 官方尚未提供矩陣乘法的相關實現。
特別備註[編輯 | 編輯原始碼]
通過特別設計__add__
方法,我們可以讓加法不滿足交換律!事實上,讀者可以注意到,字符串的加法就是不可交換的。
在規定運算符時,一方面要注意運算符只是個符號:沒必要非得讓+
對應加法,對應字符串的拼接也是可以的。另一方面要保證符號和其對應的運算易於記憶,不要讓+
去對應一般認為的減法運算。
NotImplemented[編輯 | 編輯原始碼]
注意到二元運算的調用順序。我們考慮以下情況:
class mat():
...
def __mul__(self, other):
...
if isinstance(other, (int,float,complex)):
#数量乘法
...
def __rmul__(self, other):
...
if isinstance(other, (int,float,complex)):
return self.__mul__(other)
...
a = 3
m = mat(...)
am = a * m #作数量乘法
此時,Python 顯然會先嘗試調用 a.__mul__
。而我們知道 int
類型是具有 __mul__
方法的。為什麼這段代碼沒有報錯呢?
Python 應對這種方法時,添加了一個常量:NotImplemented
。當某個方法,(例子中是 a.__mul__
) 返回 NotImplemented
時,Python 就會像 a.__mul__
不存在一樣,繼續嘗試調用下一個可能的方法(例子中是 m.__rmul__
)。
對於讀者所自定義的類,在設計運算相關的方法時,應當保證「在與其它類的實例進行計算時,如果方法未涉及到與該類型實例的運算,則應當返回 NotImplemented
」。
比較運算[編輯 | 編輯原始碼]
比較運算的方法調用和二元運算類似。但它沒有對調後的版本。
這些方法返回的內容不需要是布爾值。當它不返回布爾值時,如果比較運算的結果要被用於 if
語句或布爾運算等,Python 會對結果使用 bool()
函數,將其轉化為布爾值來確定結果為 True
還是 False
。
名稱 | 運算符 函數 |
對應方法 |
---|---|---|
小於 | a<b |
a.__lt__(self, other)
|
小於等於 | a<=b |
a.__le__(self, other)
|
大於 | a>b |
a.__gt__(self, other)
|
大於等於 | a>=b |
a.__ge__(self, other)
|
等於 | a==b |
a.__eq__(self, other)
|
不等於 | a!=b |
a.__ne__(self, other)
|
「等於」運算[編輯 | 編輯原始碼]
如果不定義該方法,默認該方法為:
True if self is other else NotImplemented
is / not is[編輯 | 編輯原始碼]
x is y
運算會比較兩個對象的 id
。簡單來講,這個運算的結果就是 id(x) == id(y)
的結果。
「不等於」運算[編輯 | 編輯原始碼]
如果不定義該方法,默認該方法為:
False if self.__eq__(other) != NotImplemented else NotImplemented
除「不等於」運算和「等於」運算在默認情況下有隱含關係外,其它比較運算在默認情況下沒有任何隱含關係。例如:x < y or x == y
為真,不代表 x <= y
為真。
functools.total_ordering[編輯 | 編輯原始碼]
該裝飾器放置在類聲明前,會自動為符合要求的類型補全比較運算的方法。具體來講,該類型必須包含:
__eq__
方法__lt__
/__le__
/__gt__
或__ge__
四者之一
它會按照易於理解的方式補全未定義的方法,使得「x < y or x == y
為真,等同於 x <= y
為真」。
具體來講,使用的大致方式如下:
from functools import total_ordering
@total_ordering
class ClassName:
...
def __eq__(self, other):
...
def __lt__(self, other):
...
比較運算與hash[編輯 | 編輯原始碼]
object.__hash__(self)[編輯 | 編輯原始碼]
該方法用於支持 hash()
函數。常見的方法是把實例的所有屬性打包成元組,並進行哈希操作,比如:
class Student:
...
def __hash__(self):
return hash( (self.Name, self.Gender, self.ID) )
擁有哈希操作的類型不應是可變類型。
如果一個類沒有定義 __eq__ 方法,則它也不應定義 __hash__ 操作;如果它定義了 __eq__ 方法但沒有定義 __hash__ 方法,則其實例將不可被用作可哈希多項集的條目(比如 set, frozenset 的元素,以及 dict 的鍵)。
讀者定義的類會默認帶有 __eq__ 和 __hash__ 方法。如果在這兩個方法都使用默認,則 __hash__ 方法會返回一個恰當的值,使得 x == y 同時意味著 x is y 和 hash(x) == hash(y)。
一個類如果自定義了 __eq__ 方法而沒有定義 __hash__ 方法,則其默認的 __hash__ 方法會被自動更改為 None 變量。