抽象是隱藏多余細節的藝術。在面向對象的概念中,抽象的直接表現形式通常為類。Python基本上提供了面向對象編程語言的所有元素,如果你已經至少掌握了一門面向對象語言,那么利用Python進行面向對象程序設計將會相當容易。
一、封裝
面向對象程序設計中的術語對象(Object)基本上可以看做數據(特性)以及由一系列可以存取、操作這些數據的方法所組成的集合。傳統意義上的“程序=數據結構+算法”被封裝”掩蓋“並簡化為“程序=對象+消息”。對象是類的實例,類的抽象則需要經過封裝。封裝可以讓調用者不用關心對象是如何構建的而直接進行使用。
一個簡單的Python類封裝如下:
Encapsulation_metaclass_=type # 確定使用新式類
class Animal:
def __init__(self): #構造方法 一個對象創建后會立即調用此方法
self.Name="Doraemon"
print self.Name
def accessibleMethod(self): #綁定方法 對外公開
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中的類包含了一般面向對象編程語言的主要元素,比如構造方法、綁定方法、靜態方法、屬性等,如果深入下去,還可以發現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): #重寫超類的綁定方法
## print "Garfield never moves more than 1m."
class RobotCat(Animal):
def __init__(self): #重寫超類的構造方法
self.Name="Doraemon"
## def move(self,meters): #重寫超類的綁定方法
## 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.Name
robot.eat("cookies") #從動物繼承而來的eat
robot.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.Name
robot.eat("cookies") #從動物繼承而來的eat
robot.move(10000000) #本來是要從機器繼承move方法,但是因為繼承的順序,這個方法直接繼承自動物而pass掉
我們為動物和機器各實現一個相同名稱的move方法,但是輸出因為繼承的順序而有所不同,Python在查找給定方法或者特性時訪問超類的順序被稱為MRO(Method Resolution Order,方法判定順序)。
三、多態
多態意味着可以對不同的對象使用同樣的操作,但它們可能會以多種形態呈現出結果。在Python中,任何不知道對象到底是什么類型,但又需要對象做點什么的時候,都會用到多態。
能夠直接說明多態的兩段示例代碼如下:
1、方法多態
Method-Polymorphism_metaclass_=type # 確定使用新式類
class calculator:
def count(self,args):
return 1
calc=calculator() #自定義類型
from random import choice
obj=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+y
print add(1,2) #輸出3
print add("hello,","world") #輸出hello,world
print 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" }); // Ok
add({ favoriteColor: "blue" }); // Error, name required
add({ 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/