抽象是隱藏多餘細節的藝術。在物件導向的概念中,抽象的直接表現形式通常為類。Python基本上提供了物件導向程式設計語言的所有元素,如果你已經至少掌握了一門物件導向語言,那麼利用Python進行物件導向程式設計將會相當容易。
一、封裝
物件導向程式設計中的術語對象(Object)基本上可以看做資料(特性)以及由一系列可以存取、操作這些資料的方法所組成的集合。傳統意義上的“程式=資料結構+演算法”被封裝”掩蓋“並簡化為“程式=對象+訊息”。對象是類的執行個體,類的抽象則需要經過封裝。封裝可以讓調用者不用關心對象是如何構建的而直接進行使用。
一個簡單的Python類封裝如下:
Encapsulation_metaclass_=type # 確定使用新式類class Animal: def __init__(self): #構造方法 一個對象建立後會立即調用此方法 self.Name="Doraemon" print self.Name def accessibleMethod(self): #Binder 方法 對外公開 print "I have a self! current name is:" print self.Name print "the secret message is:" self.__inaccessible() def __inaccessible(self): #私人方法 對外不公開 以雙底線開頭 print "U cannot see me..." @staticmethod def staticMethod(): #self.accessibleMethod() #在靜態方法中無法直接調用執行個體方法 直接拋出異常 print "this is a static method" def setName(self,name): #訪問器函數 self.Name=name def getName(self): #訪問器函數 return self.Name name=property(getName,setName) #屬性 可讀可寫
上面簡單的範例程式碼可以看出,Python中的類包含了一般物件導向程式設計語言的主要元素,比如構造方法、Binder 方法、靜態方法、屬性等,如果深入下去,還可以發現Python中內建了很多物件導向的“進階”主題,比如迭代器、反射、特性等等,提供了封裝的直接構造要素。
二、繼承1、類繼承
繼承給人的直接感覺是這是一種複用代碼的行為。繼承可以理解為它是以普通的類為基礎建立專門的類對象,子類和它繼承的父類是IS-A的關係。一個簡單而不失經典的樣本如下:
Inheritance_metaclass_=type # 確定使用新式類class Animal: def __init__(self): self.Name="Animal" def move(self,meters): print "%s moved %sm." %(self.Name,meters) class Cat(Animal): #Cat是Animal的子類 def __init__(self): #重寫超類的構造方法 self.Name="Garfield"## def move(self,meters): #重寫超類的Binder 方法## print "Garfield never moves more than 1m."class RobotCat(Animal): def __init__(self): #重寫超類的構造方法 self.Name="Doraemon"## def move(self,meters): #重寫超類的Binder 方法## print "Doraemon is flying."obj=Animal()obj.move(10) #輸出:Animal moved 10m.cat=Cat()cat.move(1) #輸出:Garfield moved 1m.robot=RobotCat()robot.move(1000) #輸出:Doraemon moved 1000m.
一個顯而易見的特點是,Python的物件導向的繼承特徵是基於類(class)的,比javascript基於原型(prototype)的繼承更容易組織和編寫代碼,也更容易讓人接受和理解。
ps:最近幾天Anders大神搞出來的TypeScript橫空出世,看它的語言規範裡繼承也是基於類的:
TypeScript-SimpleInheritanceclass Animal { constructor(public name) { } move(meters) { alert(this.name + " moved " + meters + "m."); }}class Snake extends Animal { constructor(name) { super(name); } move() { alert("Slithering..."); super.move(5); }}class Horse extends Animal { constructor(name) { super(name); } move() { alert("Galloping..."); super.move(45); }}var sam = new Snake("Sammy the Python")var tom: Animal = new Horse("Tommy the Palomino")sam.move()tom.move(34)
感覺基於類的繼承的語言好像在實現OO的可讀性和編程體驗上都不太差,據傳說javascript是碼農最想噴也是噴過最多f**k的語言。
2、多重繼承
不同於C#,Python是支援多重類繼承的(C#可繼承自多個Interface,但最多繼承自一個類)。多重繼承機制有時很好用,但是它容易讓事情變得複雜。一個多重繼承的樣本如下:
MultInheritance_metaclass_=type # 確定使用新式類class Animal: def eat(self,food): print "eat %s" %food class Robot: def fly(self,kilometers): print "flyed %skm." %kilometers class RobotCat(Animal,Robot): #繼承自多個超類 def __init__(self): self.Name="Doraemon"robot=RobotCat() # 一隻可以吃東西的會飛行的叫哆啦A夢的機器貓print robot.Namerobot.eat("cookies") #從動物繼承而來的eatrobot.fly(10000000) #從機器繼承而來的fly
如你所看到的那樣,多重繼承的好處顯而易見,我們可以輕而易舉地通過類似“組合”的方式複用代碼構造一個類型。
有個需要注意的地方,即如果一個方法從多個超類繼承,那麼務必要小心繼承的超類(或者基類)的順序:
MultInheritance_metaclass_=type # 確定使用新式類class Animal: def eat(self,food): print "eat %s" %food def move(self,kilometers): #動物的move方法 pass class Robot: def move(self,kilometers): #機器的move方法 print "flyed %skm." %kilometers class RobotCat(Animal,Robot): #繼承自多個超類 如方法名稱相同,注意繼承的順序#class RobotCat(Robot,Animal): def __init__(self): self.Name="Doraemon"robot=RobotCat() # 一隻可以吃東西的會飛行的叫哆啦A夢的機器貓print robot.Namerobot.eat("cookies") #從動物繼承而來的eatrobot.move(10000000) #本來是要從機器繼承move方法,但是因為繼承的順序,這個方法直接繼承自動物而pass掉
我們為動物和機器各實現一個相同名稱的move方法,但是輸出因為繼承的順序而有所不同,Python在尋找給定方法或者特性時訪問超類的順序被稱為MRO(Method Resolution Order,方法判定順序)。
三、多態
多態意味著可以對不同的對象使用同樣的操作,但它們可能會以多種形態呈現出結果。在Python中,任何不知道對象到底是什麼類型,但又需要對象做點什麼的時候,都會用到多態。
能夠直接說明多態的兩段範例程式碼如下:
1、方法多態
Method-Polymorphism_metaclass_=type # 確定使用新式類class calculator: def count(self,args): return 1calc=calculator() #自訂類型from random import choiceobj=choice(['hello,world',[1,2,3],calc]) #obj是隨機返回的 類型不確定#print type(obj)print obj.count('a') #方法多態
對於一個臨時對象obj,它通過Python的隨機函數取出來,不知道具體類型(是字串、元組還是自訂類型),都可以調用count方法進行計算,至於count由誰(哪種類型)去做怎麼去實現我們並不關心。
有一種稱為”鴨子類型(duck typing)“的東西,講的也是多態:
DuckTyping_metaclass_=type # 確定使用新式類class Duck: def quack(self): print "Quaaaaaack!" def feathers(self): print "The duck has white and gray feathers." class Person: def quack(self): print "The person imitates a duck." def feathers(self): print "The person takes a feather from the ground and shows it." def in_the_forest(duck): duck.quack() duck.feathers() def game(): donald = Duck() john = Person() in_the_forest(donald) in_the_forest(john) game()
就in_the_forest函數而言,參數對象是一個鴨子類型,它實現了方法多態。但是實際上我們知道,從嚴格的抽象來講,Person類型和Duck完全風馬牛不相及。
2、運算子也多態
Operator-Polymorphismdef add(x,y): return x+yprint add(1,2) #輸出3print add("hello,","world") #輸出hello,worldprint add(1,"abc") #拋出異常 TypeError: unsupported operand type(s) for +: 'int' and 'str'
上例中,顯而易見,Python的加法運算子是”多態“的,理論上,我們實現的add方法支援任意支援加法的對象,但是我們不用關心兩個參數x和y具體是什麼類型。
一兩個範例程式碼當然不能從根本上說明多態。普遍認為物件導向最有價值最被低估的特徵其實是多態。我們所理解的多態的實現和子類的虛函數地址綁定有關係,多態的效果其實和函數地址運行時動態綁定有關。在C#中實現多態的方式通常有重寫和重載兩種,從上面兩段代碼,我們其實可以分析得出Python中實現多態也可以變相理解為重寫和重載。在Python中很多內建函數和運算子都是多態的。
號外:在C#中,我們熟知介面(Interface)和多態相關,在處理多態對象時,只要關心它的介面(或者稱為“協議”)而不需要顯式指定特定實作類別型即可,這也是IoC中面向介面(抽象)而不依賴於具體實現編程的基礎。可惜在Python中根本沒有Interface(有抽象類別)。而在TypeScript的Specification中引入了Interface的概念:
TypeScript-Interfaceinterface Friend { name: string; favoriteColor?: string;}function add(friend: Friend) { var name = friend.name;}add({ name: "Fred" }); // Okadd({ favoriteColor: "blue" }); // Error, name requiredadd({ name: "Jill", favoriteColor: "green" }); // Ok
TypeScript語言規範裡對Interface的定義是命名的物件類型(Programmers can give names to object types; we call named object types interfaces. )。它直接等價於下面的Ojbect Type:
var Friend: () => { name: string; favoriteColor?: string;};
上面這個Object Type在Playgound中等價的javascript代碼如下:
var Friend;
在TypeScript編程規範文檔裡,對Interface的一些說明:
Interfaces provide the ability to give names to object types and the ability to compose existing named object types into new ones.
Interfaces have no run-time representation—they are purely a compile-time construct. Interfaces are particularly useful for documenting and validating the required shape of properties, objects passed as parameters, and objects returned from functions.
Interface可以繼承自Interface:
TypeScript-InterfaceInheritInterfaceinterface Mover{ move(): void; getStatus(): { speed: number; };}interface Shaker{ shake(): void; getStatus(): { frequency: number; };}interface SimpleMover extends Mover{}interface MoverShaker extends Mover, Shaker{ getStatus(): { speed: number; frequency: number; };}
我們嘗試讓類繼承自Interface:
TypeScript-ClassInheritInterfaceinterface Mover{ move(): void;}class SimpleMover extends Mover{ move() {alert("move") }}var mover=new SimpleMover();mover.move();
實踐證明類是不能繼承實現Interface的:A export class may only extend other classes, Mover is an interface.
現在大家知道TypeScript裡的Interface和我們所認識和理解的介面的區別了嗎?相同的一個名詞Interface,不同語境下意義並不完全相同,看上去好像是挺傻的,但是學習和使用語言不做對比幾乎是不可能的。
參考:
http://www.python.org/
http://zh.wikipedia.org/wiki/Duck_typing
http://typescript.codeplex.com/
http://www.typescriptlang.org/Playground/
http://www.nczonline.net/blog/2012/10/04/thoughts-on-typescript/