《Python基礎教程》第7章 更加抽象

weixin_34148456發表於2015-01-14

第7章 更加抽象

本章將會介紹如何建立物件,以及多型、封裝、方法、特性、超類以及繼承等概念。

物件的魔力

多型

多型字面意思為有多種形態,在程式語言中意味著即使不知道物件的具體型別,也可以進行某些操作,並且根據物件型別的不同而表現出不同的行為。

為什麼會需要多型呢?如下嘗試用一個例子來說明多型的好處(原書例子有點不好理解,這裡新舉一個例子)。假定某購物網站的商品對應程式類Product, 現在要求計算某個顧客購物車內的所有物品,計算每個物品的價格可能會有如下方法:

def getPrice(p):
    if p.type == 'type1':
        return p.price
    elif p.type == 'type2':
        return p.price - discountCalculate(p) #促銷商品
    elif p.type == 'type3':
        return p.price + taxCalculate(p) #附加稅商品

當出現一種價格計算方式的時候我們要修改程式碼,增加新的分支,分支多的時候會變得難於管理。如果專案已經被整合使用修改會更加麻煩。這時候多型的好處就體現出來了,每種商品都有自己的價格,那麼只要呼叫product.getPrice就能呼叫具體每種商品的價格計算方法。

在Python中我們已經接觸過的多型的運用,比如:

>>> o = 'abc'
>>> o.count('a')
1
>>> o = ['a', 'b', 'c']
>>> o.count('a')
1

其實不只是方法,很多內建的運算子都實現了多型:

>>> 'a' + 'b'
'ab'
>>> 1+2
3
封裝

封裝能對外部作用域隱藏內部實現細節只知道如何呼叫即可。

繼承

繼承是想要擴充套件功能,有不想寫重複的做法。

類和型別

類到底是什麼

類即型別或者種類,所有的物件都屬於某一個類,稱為類的例項。比如,鳥是鳥類的例項,鳥類有可能有很多子類(subclass)如“百靈鳥”;反過來,鳥類是“百靈鳥”的超類(superclass)。

在物件導向程式設計中,所有例項都包含類的方法,所有子類都必須繼承或者過載超類的方法,並且子類中可以新增自己特有的方法。

建立自己的類
__metaclass__ =  type

class Person:
    def setName(self, name):
        self.name = name
    def getName(self):
        return self.name
    def greet(self):
        print 'Hello, I am %s' % self.name

foo =  Person()
bar =  Person()
foo.setName('foo')
print foo.getName()

bar.name = 'bar'
print bar.name

foo.greet()


$ python class.py
foo
bar
Hello, I am foo

如上為類的建立和使用,注意第一行程式碼:宣告新式類需要在模組或者指令碼開始地方放置該行(不細究)。

特性、函式和方法
class Bird:
    song = 'Squaawk'
    def sing(self):
        print self.song
        
bird = Bird()
bird.sing()
singsong = bird.sing
singsong()

$ python class.py
Squaawk
Squaawk

某些程式語言覺得直接通過'物件.屬性'的方式訪問破壞了封裝性,但Python並不支援私有形式(儘管有一些小技巧可以達到私有特性的效果,不是目前學習的重點先跳過)。

類的名稱空間

所有位於class中間的程式碼都在類名稱空間中,這個名稱空間可以由所有的類的例項訪問。需要注意類的定義就是執行程式碼塊,因此如下定義方式雖然傻但是是可以的:

class C:
    print 'Class C is defined...'

$ python class.py
Class C is defined...

在類中可以定義供所有成員訪問的變數,如下是一個例子(在Java中叫類變數,通常在定義指明MemberCounter.members;而在Python中定義是直接寫在members類中。兩者呼叫的時候同樣使用MemberCounter.members):

class MemberCounter:
    members = 0
    def init(self):
        MemberCounter.members += 1

m1 = MemberCounter()
m1.init()
print m1.members

m2 = MemberCounter()
m2.init()
print m2.members


$ python class.py
1
2
呼叫超類

子類可以擴充套件超類的定義,既可以覆寫超類的方法,也可以直接使用超類方法而不用自己再定義:

class Filter():
    def init(self):
        self.blocked = []
    def filter(self, sequence):
        return [x for x in sequence if x not in self.blocked]

class SPAMFilter(Filter):
    def init(self):
        self.blocked = ['SPAM']

filter = Filter()
filter.init()
print filter.filter([1,2,3])

spamFilter = SPAMFilter()
spamFilter.init()
print spamFilter.filter(['SPAM','SPAM', 'zip','lotus','SPAM']);

$ python class.py
[1, 2, 3]
['zip', 'lotus']

Filter類的blocked為[]因此不會對任何內容進行過濾;而在子類SPAMFilter中,我們覆寫了init方法,仍舊使用超類的filter方法,因此所有'SPAM'的字串都被過濾掉了。

介紹幾個和類相關的常用方法
s = SPAMFilter()

#想要知道一個類是否是另一個類的子類:
print issubclass(SPAMFilter, Filter) #True
print issubclass(Filter, SPAMFilter) #False

#想要知道一個類已知基類(們):
print SPAMFilter.__bases__ # (<class __main__.Filter at 0x1004b8c18>,)

#想要知道一個物件是否是一個類的例項:
print isinstance(s, SPAMFilter) #True
print isinstance(s, Filter) #True

#想要知道一個物件屬於哪個類
print s.__class__ #__main__.SPAMFilter

多個超類

類可以繼承自多個超類,除非非常熟悉否則應該儘量避免使用,如下是一個多重繼承的例子:

class Calculator:
    def calculate(self, expression):
        self.value = eval(expression)
        
class Talk():
    def talk(self):
        print 'My value is: %s' % self.value
        
class CalculatorTalk(Calculator, Talk):
    pass
    
ct = CalculatorTalk()
ct.calculate('2+3')
ct.talk()

$ python class.py
My value is: 5

介面和內省

介面的概念和多型有關,即處理多型物件時候只需要關係公開的方法和特性,因此需要檢測物件是否真正實現了方法和特性。如下介紹兩個檢測的方法,第一個是所需方法是否存在,第二個是是所需方法否可呼叫。

hasattr(ct, 'talk') # True
hasattr(ct, 'test') # False
callable(getattr(ct, 'talk', None)) # True
callable(getattr(ct, 'test', None)) # False


相關文章