Python面向對象編程(上)
Python不僅支持面向過程編程,同時也支持面向對象編程。面向工程就是分析解決問題所需的步驟,然後用函數把這些步驟逐一實現,使用的時候再一個個調用函數就可以。面向對象則是把解決的問題按照一定規則劃分為多個獨立的對象,然後通過調用對象的方法來解決問題。在編寫小程序(少於500行代碼)時,使用面向過程編程基本上不會有任何問題。但對於中等和大型項目來說,面向對象將給代碼的編寫和維護帶來很多優勢。本文主要介紹Python中面向對象的基本概念。
面向對象基本概念
面向對象的特點主要可以概括為封裝性、繼承性、多態性。
(1)封裝性
封裝是面向對象的核心思想,將對象的屬性和行為封裝起來,不需要讓外界知道具體實現細節,這就是封裝的思想。比如,用戶使用電腦,只需要會敲鍵盤就可以,無須知道電腦內部是如何工作的。
(2)繼承性
繼承性主要描述的是類與類之間的關系,通過繼承,可以在無須重新編寫原有類的情況下,對原有類的功能進行擴展。
(3)多態性
多態性指的是在程序中允許重名現象,它指在一個類中定義的屬性和方法被其他類繼承後,它們可以具有不同的數據類型或表現出不同的行為,這使得同一個屬性和方法在不同的類中具有不同的語義。
面向對象的思想需要通過大量的實踐去學習和理解,才能將面向對象真正理解清楚。後面會通過一些例子介紹其使用方法。
類和對象基本概念
面向對象的思想中提出了兩個概念,即類和對象。類是對某一類事物的抽象描述,是一種抽象的數據類型,一種模板。而對象用於表示現實中該類事物的個體,也就是具體化了類的描述。它們的關系是,對象是類的具體實例,類是對象的模板。對象根據類創建,一個類可以創建多個對象。比如我定義了一個學生類,那麽通過類創建出來的小明、小王就叫對象。
類的定義
在面向對象的思想中最核心的就是對象,在程序中創建對象,首先需要定義一個類。Python中使用class關鍵字定義一個類,類的主體由屬性(變量)和方法(函數)組成。通過定義一個水果類來看下類的定義格式,如下:
#-*- coding:utf-8 -*- #類的創建 class Fruit(object): def __init__(self, name, color): #__init__為類的構造函數 self.name = name #實例變量 self.color = color #實例變量 def grow(self): #grow函數為實例方法。類的函數至少有1個self參數 print "Fruit grow..."
其中,Fruit是類名,類名的首字母一般是大寫。price變量在實例方法和構造方法之外,稱為類屬性。__init__(self)是構造函數,構造函數中的name和color變量稱為實例屬性,grow(self)是實例方法,實例方法中參數至少帶一個self參數。
對象的創建和使用
定義完類之後,就可以根據類來創建實例對象,創建對象的過程稱為實例化。當一個對象被創建後,包含3個方面的特性:對象的句柄、屬性和方法。對象的句柄用於區分不同的對象,當對象被創建後,該對象會獲取一塊存儲空間,存儲空間的地址即為對象的標識。對象的屬性和方法與類的成員變量和成員函數相對應。Python實例化是通過類名加圓括號的方式。實例化上述定義的水果類,格式如下:
#-*- coding:utf-8 -*- class Fruit(object):def __init__(self, name, color): #構造函數 self.name = name #實例變量 self.color = color #實例變量 def grow(self): print "Fruit grow..." if __name__ == ‘__main__‘: apple = Fruit(‘apple‘,‘red‘) #實例化對象apple print apple.name # 利用對象直接獲取對象屬性name。輸出apple print apple.color # 利用對象直接獲取對象屬性color。輸出red apple.grow() # 利用對象直接調用對象方法,不需要給參數self賦值。輸出Fruit grow...
實例化對象之後,就可以通過對象直接調用對象的屬性和方法。但是註意的是對象調用方法時,不需要給參數self賦值,self參數用於表示指向實例對象本身。到這裏,已經學會了類的定義和對象創建方法和使用了。上述的例子中僅介紹了類的基本實例屬性和實例方法的定義,實際上實例變量還區私有屬性和公有屬性,還有類變量等概念。類中的方法還包括靜態方法、類方法。
類的屬性
類的屬性是對數據的封裝,類的屬性包括實例屬性、類屬性兩種。實例屬性又區分為實例私有屬性和實例公有屬性。不同的屬性類型調用方法也不同。
(1)實例私有屬性和實例公有屬性
實例屬性一般分為私有屬性和公有屬性,不同於Java、C++等語言通過使用保護類型的修飾符去區分私有屬性和公有屬性,由於Python沒有保護類型的修飾符,Python類的私有屬性和共有屬性是通過約定屬性名稱來達到數據封裝的目的。如果屬性的名字以兩個下劃線開始,就表示為私有屬性。反之,則表示公有屬性。
#-*- coding:utf-8 -*- class Fruit(object): def __init__(self, name, color): self.__name = name #實例私有變量使用雙下劃綫開始 self.color = color def grow(self): print "Fruit grow..." if __name__ == ‘__main__‘: apple = Fruit(‘apple‘,‘red‘) #實例化水果類 #print apple.__name #不能直接報錯AttributeError: ‘Fruit‘ object has no attribute ‘__name‘ print apple._Fruit__name #apple print apple.color #red
實例的私有屬性不能使用實例直接訪問,Python解釋器會報錯提示屬性不存在。Python提供了直接訪問私有屬性的方式,可用於程序的測試和調試。私有屬性訪問的格式如下所示:
instance._classname__attribute
也就是實例名._類名__屬性名的方式。如例子中的apple._Fruit__name。但是這種直接暴露數據的做法是不提倡的。因為這種方式可以隨意更改實例屬性的值,會導致程序數據安全方面的問題。這種訪問方式主要用於開發階段的測試或調試時使用。通過的做法是定義相關的get方法獲取實例屬性的值。例如在Fruit類中定義getColor()方法,後續會介紹方法的定義和使用。
(2)實例屬性和函數變量
Python的屬性分為實例屬性和靜態屬性。實例屬性是以self作為前綴的屬性。__init__方法即Python類的構造函數。如果__init__方法中定義的變量沒有使用self作為前綴聲明,則該變量只是普通的局部變量。類中其他方法定義的變量也只是函數變量,而非類的實例屬性。
#-*- coding:utf-8 -*- class Fruit(object): def __init__(self, name, color, zone): self.__name = name #name、color變量加前綴self表示實例屬性,可以被實例化對象調用 self.color = color zone = zone #zone變量不加前綴self,只是普通的函數變量,不能被實例化對象調用 def grow(self): print "Fruit grow..." if __name__ == ‘__main__‘: apple = Fruit(‘apple‘,‘red‘, ‘Shannxi‘) #實例化水果類 print apple._Fruit__name #apple print apple.color # red #print apple.zone # 報錯AttributeError: ‘Fruit‘ object has no attribute ‘zone‘
例子中定義的函數變量zone不能被Fruit的實例化對象調用。
(2)類變量
Java、C#中有一類特殊的屬性稱為靜態變量。靜態變量可以被類直接調用,而不被實例化對象調用。當創建新的實例化對象後,靜態變量並不會獲得新的內存空間,而是使用類創建的內存空間。因此,靜態變量能夠被多個實例化對象共享。在Python中靜態變量稱為類變量,類變量可以在該類的所有實例中被共享。
#-*- coding:utf-8 -*- class Fruit(object): price = 0 #類變量 def __init__(self, name, color, zone): self.__name = name #name、color變量加前綴self表示實例屬性,可以被實例化對象調用 self.color = color zone = zone #zone變量不加前綴self,只是普通的變量,不能被實例化對象調用 def grow(self): print "Fruit grow..." if __name__ == ‘__main__‘: apple = Fruit(‘apple‘,‘red‘, ‘Shannxi‘) #實例化水果類 print Fruit.price # 0 Fruit.price = Fruit.price + 100 print "apple‘s prices = %d" % (apple.price) #100 banana = Fruit(‘banana‘, ‘yellow‘, ‘Hainan‘) print "banana‘s prices = %d" % (banana.price) #100 apple.price = 150 #修改apple對象中price的值,與banana對象無關。 print "apple‘s prices = %d" % (apple.price) #150 print "banana‘s prices = %d" % (banana.price) #100 banana.price = 200 #修改banana對象中price的值,與apple對象無關 print "apple‘s prices = %d" % (apple.price) #150 print "banana‘s prices = %d" % (banana.price) #200 Fruit.price = Fruit.price + 100 #使用類調用修改price值,不影響apple、banana對象中price的值 print "apple‘s prices = %d" % (apple.price) #150 print "banana‘s prices = %d" % (banana.price) #200 print Fruit.price #200
Python對象類的屬性和方法定義次序並沒有要求。合理的方式是把類屬性定義在類中最前面,然後再定義私有方法,最後定義公有方法。
類的方法
(1)普通方法和靜態方法
類的方法也分為公有方法和私有方法。私有方法不能被模塊外的類或方法調用,私有方法也不能被外部的類或函數調用。C#、Java中的靜態方法使用關鍵字static聲明,而Python使用函數staticmethod()或@staticmethod修改器把普通的函數轉換為靜態方法。Python的靜態方法並沒有和類的實例進行綁定,要調用只需使用類名作為它的前綴即可。下面這段代碼演示了類的方法和靜態方法的使用。
#-*- coding:utf-8 -*- class Fruit(object): price = 100 #類屬性 def __init__(self, name, color, zone): self.__name = name self.color = color def getColor(self): print self.color def getName(self): print self.__name def setColor(self, color): self.color = color def setName(self, name): self.__name = name @staticmethod #方法一,定義類的靜態方法,調用類變量,getPrice中不帶self參數 def getPrice(): print Fruit.price def getPrice2(): print Fruit.price tran_getPrice2 = staticmethod(getPrice2) ##方法二,定義類的靜態方法,調用類變量 if __name__ == ‘__main__‘: apple = Fruit(‘apple‘,‘red‘, ‘Shannxi‘) #實例化水果類 apple.getColor() # red apple.getName() #apple apple.setColor(‘green‘) apple.getColor() #green apple.setName(‘new_apple‘) apple.getName() #new_apple Fruit.getPrice() Fruit.tran_getPrice2()
上述代碼中getColor()方法中有1個self參數,該參數是區別方法和函數的標誌。類的方法至少需要1個參數,調用方法時不必給改參數賦值。通過長這個特殊的參數被命名為self,self參數表示指向實例對象本身。self參數用於區分函數和類的方法。self參數等價於Java、C#中的this關鍵字,但self必須顯示使用,因為Python是動態語言,沒有提供聲明變量的方式,這樣就無法知道在方法中要賦值的變量是不是局部變量或是否需要保存成實例屬性。當調用Fruit類的getColor()方法時,Python會把函數的調用轉換為grow(fruit)。Python自動完成了對象fruit的傳遞任務。
(2)類方法
Python中還有一種方法稱為類方法。類方法是將類本身作為操作對象的方法。類方法可以使用函數classmethod()或@classmethod修飾器定義。而與實力方法不同的是,把類作為第一個參數(cls)傳遞。把上述程序的靜態方法修改為類方法,修改後的代碼如下:
@classmethod #方法一,定義類方法,調用類變量,getPrice中不帶self參數 def getPrice(cls): print cls.price def getPrice2(cls): print cls.price tran_getPrice2 = classmethod(getPrice2) ##方法二,定義類方法,調用類變量
可見,類方法的使用和靜態方法十分相似。如果某個方法需要被其他實例共享,同時又需要使用當前實例的屬性,則定義為類方法。
Python面向對象編程(上)