Python數據結構與算法--面向對象


前面已經講過,Python是一種面向對象的編程語言. 面向對象編程語言中最重要的特征是允許程序員創建類建立數據模型來解決問題.

我們之前利用抽象數據類型提供的邏輯來描述數據對象 (它的狀態) 和功能 (它的方法). 通過構建類來實現抽象數據類型, 一個程序員可以發揮抽象處理的優勢,同時提供詳細的現實信息來解決問題.當我們想實現一個抽象數據類型的時候,我們將構建一個新的類.

本文地址:http://www.cnblogs.com/archimedes/p/python-datastruct-algorithm-class.html,轉載請注明源地址。

一個分數類

下面來看一個非常普通的例子,用來展示實現抽象數據類型的一個用戶自定義類:Fraction(分數). 我們已經知道 Python 給我們提供了大量的類. 有很多可以適當地幫我們構建分數類型的數據對象.

一個分數比如3/5包含兩部分. 分子,可以為任何整數. 分母, 可以為除了0以外的任何整數.

 Fraction 類的方法應該能夠讓 Fraction 對象可以像其他的數值那樣進行計算. 我們需要可以進行分數之間的 加, 減, 乘, 和 除 運算. 更進一步, 所有的方法應該返回最簡分數.

在 Python中, 我們定義一個類的名字還有一些方法,類似於定義一個函數,例如,

class Fraction:

   #the methods go here

提供了給我們定義方法的框架.第一個方法是所有類都要提供的構造器. 構造函數定義了類創建的方式. 要創建分數對象, 我們需要提供兩部分的數據:分子和分母. 在 Python中, 構造函數使用 __init__ (兩條下划線包圍 init) ,如下所示:

Listing 2

class Fraction:

    def __init__(self,top,bottom):

        self.num = top
        self.den = bottom

注意到參數列表含有三個參數: (selftopbottom). self 是一個引用對象自身的特殊的參數. 它通常作為第一個參數; 但是, 它從不在調用的時候傳值. 之前已經講過,分數包含兩部分(分子和分母). 記號 self.num 在構造函數中被定義為 fraction 對象具有一個叫num 的內部數據對象. 同理, self.den 也是類似的目的.

要實現 Fraction 類, 我們需要調用構造函數. 接着通過類名傳遞參數 (注意到我們從不直接調用__init__). 例如:

myfraction = Fraction(3,5)

創建一個分數對象 myfraction 代表分數3/5 .


接着要做的事情就是給抽象數據類型實現方法. 首先, 意識到當我們要輸出一個 Fraction 對象.

>>> myf = Fraction(3,5)
>>> print(myf)
<__main__.Fraction instance at 0x409b1acc>

fraction 對象, myf, 並不知道怎樣響應輸出操作.  print 函數需要對象轉換為可輸出的字符串格式,這樣才能輸出. 唯一的選擇 myf 必須顯示變量實際的地址引用(自身的地址). 這不是我們想要的.

有兩種解決問題的辦法. 一種是定義一種稱為 show 的方法,可以將Fraction 對象作為一個字符串的形式打印. 我們可以實現如 Listing 3所示.假如我們按照前面講的創建 Fraction 對象, 我們可以讓它輸出自身, 換句話說, 打印自身按照適當的格式. 不幸的是, 這通常不起作用. 為了使輸出工作正常, 我們必須告訴 Fraction 類怎樣將自身轉換為字符串的格式.

Listing 3

def show(self):
     print(self.num,"/",self.den)
>>> myf = Fraction(3,5)
>>> myf.show()
3 / 5
>>> print(myf)
<__main__.Fraction instance at 0x40bce9ac>

在 Python, 所有的類都提供但不是都適用的標准的方法. 其中之一, __str__,就是一個將對象轉換為字符串的方法. 這個方法默認的實現是用來以字符串格式返回類實例的地址. 我們必須為這個方法提供一個“更好的”實現. 我們說這個新的方法重載前面的, 或者說重新定義了方法的行為.

要實現這個,我們簡單地定義一個名叫 __str__ 的方法並給出實現 如Listing 4. 這個定義除了使用特殊參數 self以外不需要其他的信息. 注意函數中的不同的實現辦法.

Listing 4

def __str__(self):
    return str(self.num)+"/"+str(self.den)
>>> myf = Fraction(3,5)
>>> print(myf)
3/5
>>> print("I ate", myf, "of the pizza")
I ate 3/5 of the pizza
>>> myf.__str__()
'3/5'
>>> str(myf)
'3/5'
>>>

我們可以為我們的新 Fraction 類覆蓋很多其他的方法. 其中一些最重要的是一些基礎的算術運算操作. 我們可以創建兩種 Fraction 對象,同時使用“+” 符號將它們相加 . 這時, 如果我們使兩分數相加, 我們得到:

>>> f1 = Fraction(1,4)
>>> f2 = Fraction(1,2)
>>> f1+f2
Traceback (most recent call last): File "<pyshell#173>", line 1, in -toplevel-
    f1+f2
TypeError: unsupported operand type(s) for +:
          'instance' and 'instance'

如果你仔細觀察錯誤信息, 你將發現問題是: “+” 操作符不能理解Fraction 操作.

我們可以通過給 Fraction 類提供重載的加法函數來實現. 在 Python, 這種方法稱為 __add__ 同時需要兩個參數. 第一個參數, self,  第二個參數是另一個操作數. 例如,

f1.__add__(f2)

當 Fraction  f1 加 Fraction f2. 可以寫成標准的形式:f1+f2.

兩個分數必須有相同的分母才能直接相加. 使它們分母相同最簡單的方法是通分: ,具體實現如 Listing 5. 加法函數返回了一個新的 Fraction 對象.

Listing 5

def __add__(self,otherfraction):

     newnum = self.num * otherfraction.den + self.den*otherfraction.num
     newden = self.den * otherfraction.den

     return Fraction(newnum,newden)
>>> f1=Fraction(1,4)
>>> f2=Fraction(1,2)
>>> f3=f1+f2
>>> print(f3)
6/8
>>>

上面的加法函數看起來實現了我們期望的, 但是還可以更完美. 注意到 6/8 是正確的結果,但是卻不是以 “最簡項” 的形式展示的. 最好的表達式為3/4. 為了使我們的結果為最簡項的形式, 我們需要一個輔助函數才化簡分數. 這個函數可以求出最大公約數, 或者稱為 GCD. 可以通過分子和分母的最大公約數來達到化簡分數的目的.

計算最大公約數最著名的算法要數 Euclid算法,原理我就不詳細指明了,很簡單。實現如下:

>>> def gcd(m, n):
    while m % n != 0:
        oldm = m
        oldn = n
        m = oldn
        n = oldm % oldn
    return n

>>> print gcd(20, 10)
10

這樣我們就可以化簡任何的分數了,代碼如下: (Listing 6).

Listing 6

def __add__(self,otherfraction):
    newnum = self.num*otherfraction.den + self.den*otherfraction.num
    newden = self.den * otherfraction.den
    common = gcd(newnum,newden)
    return Fraction(newnum//common,newden//common)
>>> f1=Fraction(1,4)
>>> f2=Fraction(1,2)
>>> f3=f1+f2
>>> print(f3)
3/4

我們的 Fraction 對象現在有兩個非常重要的方法,如上圖所示. 一些需要新增進我們的實例類 Fraction 的方法是:允許兩個分數進行比較. 假如我們有兩個 Fraction 對象, f1 和f2f1==f2 將得到True 假如他們指向同一個對象. 即使分子分母都相同,但是不滿足條件依然將不相等. 這被稱為 shallow equality (如下圖).

我們可以創建 deep equality (如上圖)–通過值相等來判斷, 不同於引用–通過覆蓋 __eq__ 方法.  __eq__ 是另一個存在於所有類中標准方法. __eq__ 方法比較兩個對象當值相等的時候返回 True ,否則返回 False.

在 Fraction 類中, 我們實現了 __eq__ 方法通過常規比較方法來比較分數 (see Listing 7). 值得注意的是還有其他的方法可以覆蓋. 例如,  __le__ 方法提供了小於等於功能.

Listing 7

def __eq__(self, other):
    firstnum = self.num * other.den
    secondnum = other.num * self.den

    return firstnum == secondnum

完整的 Fraction 類的代碼如下所示:

def gcd(m,n):
    while m%n != 0:
        oldm = m
        oldn = n

        m = oldn
        n = oldm%oldn
    return n

class Fraction:
     def __init__(self,top,bottom):
         self.num = top
         self.den = bottom

     def __str__(self):
         return str(self.num)+"/"+str(self.den)

     def show(self):
         print(self.num,"/",self.den)

     def __add__(self,otherfraction):
         newnum = self.num*otherfraction.den + \
                      self.den*otherfraction.num
         newden = self.den * otherfraction.den
         common = gcd(newnum,newden)
         return Fraction(newnum//common,newden//common)

     def __eq__(self, other):
         firstnum = self.num * other.den
         secondnum = other.num * self.den
         return firstnum == secondnum

x = Fraction(1,2)
y = Fraction(2,3)
print(x+y)
print(x == y)

運行結果:

7/6
False

您還可能感興趣:

Python基礎(10)--數字 

Python基礎(9)--正則表達式

Python基礎(8)--文件

Python基礎(7)--函數

Python基礎(6)--條件、循環 

Python基礎(5)--字典

Python基礎(4)--字符串

Python基礎(3)--列表和元組

Python基礎(2)--對象類型 

Python基礎(1)--Python編程習慣與特點

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM