Python面向對象之運算符重載
運算符重載只是意味著在類方法中攔截內置的操作,也就是說當類的實例出現在內置操作中,Python自動調用我們的方法,並且我們的方法的返回值變成了相應操作的結果。
關於重載的關鍵知識點:
- 運算符重載讓類攔截常規的Python運算
- 類可重載所有Python表達式運算符
- 類也可重載打印、函數調用、屬性點號運算等內置運算
- 重載使類實例的行為像內置類型
- 重載是通過提供特殊名稱的類方法來實現的
換句話說,當類中提供了某個特殊名稱的方法,在該類的實例出現在它們相關的表達式時,Python自動調用它們。
當然有些運算符是不能重載:賦值運算符、邏輯運算符。在C++中,賦值運算符與邏輯運算符是可以重載的。
運算符重載
接下來我們看一個實數的例子,我們都知道,實數包含實部與虛部,這個例子主要實現兩個實數的相加。代碼如下:
class Complex: def __init__(self, real, imag): self.real = real self.imag = imag def add(self, other): return Complex(self.real+other.real, self.imag+other.imag) def sub(self, other): return Complex(self.real-other.read, self.imag-other.imag) c1 = Complex(1, 1) c2 = Complex(2, 3) c3 = c1.add(c2) c3.real 3 c3.imag 4 c1 + c2 # 此處出問題了,試圖讓兩個對象相加 --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-7-61818d23e61f> in <module>() ----> 1 c1 + c2 TypeError: unsupported operand type(s) for +: ‘Complex‘ and ‘Complex‘
對上面的程序稍作修改,使其增加減法運算符的重載:
class Complex: def __init__(self, real, imag): self.real = read self.imag = imag def __add__(self, other): return Complex(self.real + other.real, self.imag + other.imag) def __sub__(self, other): return Complex(self.real - other.read, self.imag - other.imag) c1 = Complex(1, 1) c2 = Complex(2, 3) c4 = c1 + c2 c4.real c4.imag
方法名前後帶雙下劃線的方法,稱之為專有方法或者魔術方法。帶雙下劃線的方法是有特殊含義的。
這時我們也不妨看看C++中是如何實現運算符重載的吧,代碼如下:
#include <iostream>
using namespace std;
class Complex {
private:
int real;
int imag;
public:
Complex(int real=0, int imag=0)
{
this->real = real;
this->imag = imag;
}
int get_real()
{
return this->real;
}
int get_imag()
{
return this->imag;
}
Complex operator+(const Complex& c)
{
Complex complex;
complex.real = this->real + c.real;
complex.imag = this->imag + c.imag;
return complex;
}
};
int main(int argc, char *argv[])
{
Complex c1(1, 1);
Complex c2(2, 3);
Complex c3;
c3 = c1 + c2;
cout << "c3.real = " << c3.get_real() << endl;
cout << "c3.imag = " << c3.get_imag() << endl;
return 0;
}
編譯並運行:
$ g++ complex.cpp -o complex
$ ./complex
c3.real = 3
c3.imag = 4
通過上述兩個例子對比我們可以很容易就發現,在Python中實現運算符重載只需要很少的代碼(這個Python例子只有)就可以完成,而使用C++
或其他編譯型的語言要更多一些的代碼(本例中是50多行)。通過上面的兩種代碼的風格對比,你是否發現編程語言中的面向對象編程是否都長得很像呢?如果回答是肯定的,那麽說明我們確實理解或掌握了面向對象編程的這一類問題,而不管它是什麽編程語言。
上述我們實現了兩個對象的加法及減法運算,也就是在類中重載了加法及減法運算符。接下來再看一個例子,我們實現乘法的運算符重載。代碼如下:
class Calc:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Calc(self.x + other.x, self.y + other.y)
def __mul__(self, other):
return Calc(self.x * other.x, self.y * other.y)
>>> c1 = Calc(10, 2)
>>> c2 = Calc(8, 4)
>>> c3 = c1 + c2
>>> c3.x
18
>>> c3.y
6
>>> c4 = c1 * c2
>>> c4.x
80
>>> c4.y
8
好了,關於舉例就說這麽多。如何查看一個對象有哪些運算符可以重載呢?在交互式解釋器界面下,通過help
命令來查看。如查看int
對象可以支持的運算符重載:
Help on class int in module builtins:
class int(object)
| int(x=0) -> integer
| int(x, base=10) -> integer
|
| Convert a number or string to an integer, or return 0 if no arguments
| are given. If x is a number, return x.__int__(). For floating point
| numbers, this truncates towards zero.
|
| If x is not a number or if base is given, then x must be a string,
| bytes, or bytearray instance representing an integer literal in the
| given base. The literal can be preceded by ‘+‘ or ‘-‘ and be surrounded
| by whitespace. The base defaults to 10. Valid bases are 0 and 2-36.
| Base 0 means to interpret the base from the string as an integer literal.
| >>> int(‘0b100‘, base=0)
| 4
|
| Methods defined here:
|
| __abs__(self, /) # 絕對值
| abs(self)
|
| __add__(self, value, /) # 加法
| Return self+value.
|
| __and__(self, value, /) # 邏輯與
| Return self&value.
|
| __bool__(self, /) # 布爾
| self != 0
|
| __ceil__(...)
| Ceiling of an Integral returns itself.
|
| __divmod__(self, value, /)
| Return divmod(self, value).
|
| __eq__(self, value, /) # 相等
| Return self==value.
|
| __float__(self, /)
| float(self)
|
| __floor__(...)
| Flooring an Integral returns itself.
|
| __floordiv__(self, value, /)
| Return self//value.
| __format__(...)
| default object formatter
|
| __ge__(self, value, /) # 大於等於
| Return self>=value.
|
| __getattribute__(self, name, /)
| Return getattr(self, name).
|
| __getnewargs__(...)
|
| __gt__(self, value, /) # 大於
| Return self>value.
|
| __hash__(self, /)
| Return hash(self).
|
| __index__(self, /)
| Return self converted to an integer, if self is suitable for use as an index into a list.
|
| __int__(self, /)
| int(self)
# 省略很多
內置函數重載
內置函數也是可以重載的,如果要改變對象的默認行為,那麽就需要對它的內置函數進行重載。舉個簡單的例子:
class Person:
def __init__(this, name, age):
this.name = name
this.age = age
p = Person(‘lavenliu‘, 28)
print(p) # 打印對象時,默認打印的是其在內存中的地址
<__main__.Person object at 0x1045b7a58>
如果我們希望打印該對象時,打印其名字與年齡的話,就要重載__str__
內置方法了。上述代碼修改如下:
class Person:
def __init__(this, name, age):
this.name = name
this.age = age
def __str__(this):
return ‘{}: {}, {}‘.format(__class__.__name__, this.name, this.age)
p = Person(‘Taoqi‘, 25)
print(p)
Person: Taoqi, 25
上面我們就重載了內置方法__str__
,改變上述對象的默認輸出行為。
接下來給大家講解hash的重載,object有一個__hash__
屬性,而所有的對象都繼承自object。
print(hash(‘abc‘))
print(hash(123))
# 重載hash方法
class Point:
def __hash__(self):
return 1
print(hash(Point()))
: 3225858027842937999
: 123
: 1
使用內置函數hash對某個對象求hash值時,會調用對象的__hash__
方法。__hash__
方法必須返回int類型。hash方法可以用來做什麽呢?什麽樣的對象又是可hash的呢?
可hash對象,就是具有__hash__
方法的對象。除非明確的把hash設置為None,
class Point:
__hash__ = None
set([Point()])
: TypeError: unhashable type: ‘Point‘
一個類如果沒有重寫__hash__
方法的話,這個類的每個對象通常具有不同的hash。如下:
class Point:
pass
p = Point()
print(hash(p))
print(id(p))
print(hash(id(p)))
p2 = Point()
print(hash(p2))
print(id(p2))
: 270104426
: 4321670816
: 4321670816
: -9223372036584671309
: 4321671992
在舉一個解析集合坐標的例子,
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __hash__(self):
return hash(‘{}:{}‘.format(self.x, self.y))
p1 = Point(3, 5)
p2 = Point(3, 5)
# p1, p2都是(3,5)坐標,那麽它們應該是相同的對象
# 接下來使用set()去重
print(set([p1, p2]))
: {<__main__.Point object at 0x102077b38>, <__main__.Point object at 0x102077e80>}
由上面的輸出,我們可以看到p1與p2是不同的對象。
內置方法與對象的特殊方法
len重載
len的重載。當對象實現了len方法的時候,可以使用內置的len方
法求對象的長度。len方法必須返回非負整數。
bool重載
- 當對象o實現了
__bool__
方法時,bool(o)返回值為o.__bool__()
。 - 當對象o沒實現
__bool__
方法時,如果o實現了__len__
方法,bool(o)返回值為len(o) != 0。 - 當對象o既沒有
__bool__
方法及__len__
方法時,bool(o)返回值為True。 - 當對象o即實現
__bool__
及__len__
方法時,__bool__
的優先級更高。 __bool__
方法必須返回布爾類型。
以上對if及while進行判斷時是非常有用的。
總結
今天主要介紹了Python中運算符重載的知識點,並使用C++中的運算符重載對位對比。其實也想讓大家體會到如果把某一類問題解決了,那麽類似的問題很多都會迎刃而解,真正做到融會貫通,一通百通。
今天主要講解了:
- Python運算符重載
- Python內置方法重載(簡單介紹,後面還會有介紹)
希望以上內容對大家有幫助。歡迎大家關註DevOps技術客棧。
Python面向對象之運算符重載