《資料結構與演算法——Python語言描述》筆記(4)
第2章 抽象資料型別和python類
2.1 抽象資料型別
ADT(Abstract Data Type)是一種思想,也是一種組織程式的技術,主要包括:
1、圍繞著一類資料定義程式模組。
2、模組的介面和實現分離。
3、在需要實現時,選擇一套合適的機制,採用合理的技術,實現這種ADT的功能,包括具體的資料表示和操作。
2.2 Python中的類
本節主要講python利用class實現抽象資料型別。
class Rational_number():
def __init__(self,num,den=1):
self.num=num
self.den=den
def plus(self,another):
den=self.den*another.den
num=self.den*another.num+self.num*another.den
# 求最大公約數,保證可以被約到最簡形式
max=self.greatest_common_divisor(num,den)
den=den/max
num=num/max
# int()確保輸出的是整數
return Rational_number(int(num),int(den))
# 求兩個數的最大公約數
def greatest_common_divisor(self,a,b):
if a<b:
temp=a
a=b
b=temp
c=a%b
while c!=0:
a=b
b=c
c=a%b
return b
def print_number(self):
# str(實數)可以將實數轉化為字串
print(str(self.num)+"/"+str(self.den))
a=Rational_number(3,2)
b=Rational_number(5,2)
a.plus(b).print_number()
下面對這段程式碼做說明:
- class是類的關鍵字,後面跟類名和冒號,此行稱為類定義的頭部,後面就是類定義的體部分了。上面定義了一個有理數的類。
- 類體部分是一批函式,稱為類的方法,最重要的方法是操作本類的例項物件的方法,稱為例項方法,這種方法總是從本類的物件出發去呼叫,其引數列表裡面的第一個引數就是實際呼叫時的呼叫物件,通常以self為引數的名字。
- 在一個類裡面,__int__方法稱為初始化方法,用於構造本類的新物件,比如用a=Rational_number(3,2)建立一個物件,並賦給變數a,呼叫式應給出除self之外的其他實際引數。
- self.num形式的寫法表示本類例項物件的屬性,num是屬性名。在初始化方法中,建立本類物件的時候就會為它的屬性賦值。
- 類的其他例項方法也應該以self為第一個引數,呼叫時以例項物件名.方法名(引數)。如果只有一個self引數,則為例項物件名.方法名()。
類定義的作用是支援建立抽象的資料型別,在建立這種抽象時人們不希望暴露其實現的內部細節。如對於有理數類,不希望暴露這種物件內部是用兩個整數表示分子和分母。需要為此而隱藏一些資訊。
在python中,以單下劃線開頭的屬性名和函式名是內部使用的名字,在類外不能使用,另外,python對類定義裡面以兩個下劃線開頭(不能是結尾)的名字做了特殊處理,不能在類定義之外訪問。(類的私有成員函式或者變數)
考慮上面程式碼中求最大公約數的方法,該函式並不依賴與有理數的物件,因此第一個引數無需使用self,即它不需要作為類的例項方法出現,我們將其定義為類的非例項方法(靜態方法)
還有一個問題需要考慮,在有理數類的初始化方法中沒有檢查引數,首先要檢查是否引數均為正數,然後分母是否為0,如果不滿足,則丟擲異常。而且分子分母可能為正或負,需要將符號加到公約化簡後的分子上去。(實驗發現,如果去掉了@staticmethod沒有什麼影響。)
class Rational_number():
def __init__(self,num,den=1):
if not isinstance(num,int) or not isinstance(den,int):
raise TypeError
if den==0:
raise ZeroDivisionError
sign=1
if num<0:
num,sign=-num,-sign
if den<0:
den,sign=-den,-sign
g=Rational_number._greatest_common_divisor(num,den)
# 在python3.0中,"/"表示浮點數除法,返回浮點結果;"//"表示整數除法,返回整數結果。
self._num=sign*(num//g)
self._den=den//g
# 求兩個數的最大公約數
@staticmethod
def _greatest_common_divisor(a,b):
if b==0:
a,b=b,a
while a!=0:
a,b=b%a,a
return b
下面考慮類內其他的方法。由於兩個屬性_den和_num均被設定為內部屬性,不應該在類外引用他們,但在實際計算中,我們有時需要提取分子和分母,為此需要有一對解析的操作(也是例項方法):
def get_num(self):
return self._num
def get_den(self):
return self._den
考慮有理數的運算,人們希望用(+,-,*,/)等運算子來描述計算過程,寫出更加自然的表示式,在python中,為所有的算術運算子規定了特殊的方法名。+運算子對應了__add__,*運算子對應了__mul__,%運算子對應了__mod__,/運算子對應了__truediv__,-運算子對應了__sub__。
def __add__(self,another):
den=self._den*another._den
num=self._den*another._num+self._num*another._den
# 求最大公約數,保證可以被約到最簡形式
max=Rational_number._greatest_common_divisor(num,den)
den=den//max
num=num//max
return Rational_number(num,den)
在呼叫時,只需要用a+b即可。
有理數經常要比較大小和不等,這些類進行比較時,也提供了特殊的方法名,如下面的定義:
# 定義相等
def __eq__(self, other):
return self._den*other._num==self._num*other._den
# 定義大於
def __gt__(self, other):
return self._den * other._num < self._num * other._den
# 定義小於
def __lt__(self, other):
return self._den * other._num > self._num * other._den
為了便於輸出,人們經常在類定義裡面定義一個將該類物件轉換到字串的方法,為了保證系統的str函式可以正常使用,這個方法要採用特殊的名字__str__。內建函式str將呼叫它。
下面是完整的程式,此時的有理數類以及和python系統內部的型別沒有什麼差別了,地位和用法相同,python標準庫的一些型別就是如此定義的。
class Rational_number():
def __init__(self,num,den=1):
if not isinstance(num,int) or not isinstance(den,int):
raise TypeError
if den==0:
raise ZeroDivisionError
sign=1
if num<0:
num,sign=-num,-sign
if den<0:
den,sign=-den,-sign
g=Rational_number._greatest_common_divisor(num,den)
# 在python3.0中,"/"表示浮點數除法,返回浮點結果;"//"表示整數除法,返回整數結果。
self._num=sign*(num//g)
self._den=den//g
def __add__(self,another):
den=self._den*another._den
num=self._den*another._num+self._num*another._den
# 求最大公約數,保證可以被約到最簡形式
max=Rational_number._greatest_common_divisor(num,den)
den=den//max
num=num//max
return Rational_number(num,den)
# 定義相等
def __eq__(self, other):
return self._den*other._num==self._num*other._den
# 定義大於
def __gt__(self, other):
return self._den * other._num < self._num * other._den
# 定義小於
def __lt__(self, other):
return self._den * other._num > self._num * other._den
# 求兩個數的最大公約數
@staticmethod
def _greatest_common_divisor(a,b):
if b==0:
a,b=b,a
while a!=0:
a,b=b%a,a
return b
def get_num(self):
return self._num
def get_den(self):
return self._den
def __str__(self):
return str(self._num)+"/"+str(self._den)
def print_number(self):
# str(實數)可以將實數轉化為字串
print(self._num,"/",self._den)
a=Rational_number(3,4)
b=Rational_number(5,2)
(a+b).print_number()
print((a+b).__str__())
t=type(a)
print(t)
print(a>b)