1. 程式人生 > >《資料結構與演算法——Python語言描述》筆記(4)

《資料結構與演算法——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,即它不需要作為類的例項方法出現,我們將其定義為類的非例項方法(靜態方法)

描述時需要在函式定義的頭部行之前加上修飾符@staticmethod。靜態方法就是類裡面的普通函式,也是該類的區域性函式,呼叫時用類名.方法名(引數)

        還有一個問題需要考慮,在有理數類的初始化方法中沒有檢查引數,首先要檢查是否引數均為正數,然後分母是否為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)