標籤:作用 傳送門 __init__ log variables test 它的 否則 module
物件導向簡介
OOP編程是利用“類”和“對象”來建立各種模型來實現對真實世界的描述,使用物件導向編程的原因一方面是因為它可以使程式的維護和擴充變得更簡單,並且可以大大提高程式開發效率 ,另外,基於物件導向的程式可以使它人更加容易理解你的代碼邏輯,從而使團隊開發變得更從容。
物件導向的三大特性
Encapsulation 封裝
把客觀事物封裝成抽象的類,並且類可以把自己的屬性和方法只讓可信的類或者對象操作,對不可信的進行資訊隱藏。
注意的是。這裡說的屬性並不僅僅是基礎資料型別 (Elementary Data Type),還包括引用資料類型,也就是說,我們同樣可以在一個類中封裝其他類的對象,使其在這個類中實現引用類的相應辦法。
Inheritance 繼承
首先,什麼叫繼承呢?從理解上來說就是兒子獲得了父親所有的東西,並且這些東西是屬於兒子的,兒子可以隨意支配。那麼從程式設計語言角度出發,就是一個類擷取了另外一個類的全部方法,並且對這些方法進行自主的支配。在這裡,被別人繼承的類,我們叫父類,也叫超類或者基類。而繼承了父類的類呢,就叫子類,也叫衍生類別。
所以一個類可以派生出子類,而子類會自動繼承在這個父類裡定義的屬性、方法
Polymorphism 多態
多態的定義:指允許不同類的對象對同一訊息做出響應。即同一訊息可以根據發送對象的不同而採用多種不同的行為方式。(發送訊息就是函數調用)
實現多態的技術稱為:動態綁定(dynamic binding),是指在執行期間判斷所引用對象的實際類型,根據其實際的類型調用其相應的方法。
多態是物件導向的重要特性,簡單點說:“一個介面,多種實現”,指一個基類中派生出了不同的子類,且每個子類在繼承了同樣的方法名的同時又對父類的方法做了不同的實現,這就是同一種事物表現出的多種形態。
現實中,關於多態的例子不勝枚舉。比方說按下 F1 鍵這個動作,如果當前在 Flash 介面下彈出的就是 AS 3 的協助文檔;如果當前在 Word 下彈出的就是 Word 協助;在 Windows 下彈出的就是 Windows 協助和支援。同一個事件發生在不同的對象上會產生不同的結果。就好像糖一樣,有多種口味,你想吃什麼口味的就可以吃什麼口味。但在程式中,卻不是你想要怎樣就怎樣。更多的是需要怎樣去做就怎樣去做。來一個算是比較官方的解釋:在物件導向語言中,介面的多種不同的實現方式即為多態。
編程其實就是一個將具體世界進行抽象化的過程,多態就是抽象化的一種體現,把一系列具體事物的共同點抽象出來, 再通過這個抽象的事物, 與不同的具體事物進行對話。
對不同類的對象發出相同的訊息將會有不同的行為。比如,你的老闆讓所有員工在九點鐘開始工作, 他只要在九點鐘的時候說:“開始工作”即可,而不需要對銷售人員說:“開始銷售工作”,對技術人員說:“開始技術工作”, 因為“員工”是一個抽象的事物, 只要是員工就可以開始工作,他知道這一點就行了。至於每個員工,當然會各司其職,做各自的工作。
多態允許將子類的對象當作父類的對象使用,某父類型的引用指向其子類型的對象,調用的方法是該子類型的方法。這裡引用和調用方法的代碼編譯前就已經決定了,而引用所指向的對象可以在運行期間動態綁定
封裝1.類和對象
Class 類
一個類即是對一類擁有相同屬性的對象的抽象、藍圖、原型。在類中定義了這些對象的都具備的屬性(variables(data))、共同的方法。
對事物抽象出來類,即是封裝。
Object 對象
一個對象即是一個類的執行個體化後的執行個體,即對象是類的執行個體。一個類必須經過執行個體化後方可在程式中調用,一個類可以執行個體化多個對象,每個對象亦可以有不同的屬性,就像人類是指所有人,每個人是指具體的對象,人與人之前有共性,亦有不同
python中使用class保留字來定義類,類名的首字母一定要大寫。
以Student類為例,在Python中,定義類是通過class關鍵字:class Student(object): passclass後面緊接著是類名,即Student,類名通常是大寫開頭的單詞,緊接著是(object),表示該類是從哪個類繼承下來的,繼承的概念我們後面再講,通常,如果沒有合適的繼承類,就使用object類,這是所有類最終都會繼承的類。定義好了Student類,就可以根據Student類建立出Student的執行個體,建立執行個體是通過類名+()實現的:>>> bart = Student()>>> bart<__main__.Student object at 0x10a67a590>>>> Student<class ‘__main__.Student‘>可以看到,變數bart指向的就是一個Student的執行個體,後面的0x10a67a590是記憶體位址,每個object的地址都不一樣,而Student本身則是一個類。可以自由地給一個執行個體變數綁定屬性,比如,給執行個體bart綁定一個name屬性:>>> bart.name = ‘Bart Simpson‘>>> bart.name‘Bart Simpson‘由於類可以起到模板的作用,因此,可以在建立執行個體的時候,把一些我們認為必須綁定的屬性強制填寫進去。通過定義一個特殊的__init__方法,在建立執行個體的時候,就把name,score等屬性綁上去:class Student(object): def __init__(self, name, score): self.name = name self.score = score注意:特殊方法“__init__”前後分別有兩個底線!!!注意到__init__方法的第一個參數永遠是self,表示建立的執行個體本身,因此,在__init__方法內部,就可以把各種屬性綁定到self,因為self就指向建立的執行個體本身。有了__init__方法,在建立執行個體的時候,就不能傳入空的參數了,必須傳入與__init__方法匹配的參數,但self不需要傳,Python解譯器自己會把執行個體變數傳進去:>>> bart = Student(‘Bart Simpson‘, 59)>>> bart.name‘Bart Simpson‘>>> bart.score59
和普通的函數相比,在類中定義的函數只有一點不同,就是第一個參數永遠是執行個體變數self,並且,調用時,不用傳遞該參數。除此之外,類的方法和普通函數沒有什麼區別,所以,你仍然可以用預設參數、可變參數、關鍵字參數和命名關鍵字參數。
注意:self代表類的執行個體,而非類!
2.執行個體屬性和類屬性屬性有兩種:執行個體屬性、類屬性。
執行個體屬性是在建構函式init中定義的,定義時以self為首碼。
類屬性是在類中方法之外定義的。
執行個體屬性屬於對象(執行個體),只能在執行個體化對象後通過對象名字訪問。類屬性屬於類,可直接通過類名訪問,不用執行個體化對象(儘管也可以通過對象來訪問類屬性,但不建議這樣做,因為這樣做會導致類屬性值不一致)。類屬性還可以在類定義之後,在程式中通過類名增加,如:類名.類屬性=值
共有屬性和私人屬性
屬性名稱以雙底線__開頭的則是私人屬性,否則是共有屬性。私人屬性在類外不能直接被訪問。但python提供了訪問私人屬性的方式,可用於程式的測試和調試。方式如下:
對象名.__類名+私人屬性名稱字
例如:
class People(object): def __init__(self,name,money): self.name = name self.__money = moneyp1 = People(‘user1‘, 1000000)p1.__People__money = 999999 #訪問私人屬性並修改~print(p1.__People__money)
3.類的方法
類的方法:
在類的內部,使用 def 關鍵字來定義一個方法,與一般函數定義不同,類方法必須包含參數 self,且為第一個參數,self 代表的是類的執行個體對象。
self 的名字並不是規定不變的(因為是形參),也可以使用 this,但是最好還是按照約定是用 self。
公有方法和私人方法
公有方法、私人方法都屬於對象,每個對象都有自己的公有方法、私人方法。公有方法通過對象名調用,私人方法則不能通過對象名調用,只能在屬於該對象的方法中通過self來調用,即只能在類的內部調用 ,不能在類地外部調用。
兩個底線開頭,聲明該方法為私人方法。公有方法直接是寫一個方法名即可。
類方法、靜態方法
1.類方法通過@classmethod裝飾器實現,類方法和普通方法的區別是, 類方法只能訪問類變數,不能訪問執行個體變數
class Dog(object): def __init__(self,name): self.name = name @classmethod def eat(self): print("%s is eating" % self.name)d = Dog("hahaha")d.eat()
執行報錯如下,說Dog沒有name屬性,因為name是個執行個體變數,類方法是不能訪問執行個體變數的
Traceback (most recent call last): File "/Users/jieli/PycharmProjects/python_projects/物件導向/類方法.py", line 9, in <module> d.eat() File "/Users/jieli/PycharmProjects/python_projects/物件導向/類方法.py", line 6, in eat print("%s is eating" % self.name)AttributeError: type object ‘Dog‘ has no attribute ‘name‘
此時可以定義一個類變數,也叫name,看下執行效果
class Dog(object): name = "hahahahahahaha" def __init__(self,name): self.name = name @classmethod def eat(self): print("%s is eating" % self.name)d = Dog("hahaha")d.eat()運行輸出:hahahahahahaha is eating
2.通過@staticmethod裝飾器即可把其裝飾的方法變為一個靜態方法。普通的方法,可以在執行個體化後直接調用,並且在方法裡可以通過self.調用執行個體變數或類變數,但靜態方法是不可以訪問執行個體變數或類變數的,一個不能訪問執行個體變數和類變數的方法,其實相當於跟類本身已經沒什麼關係了,它與類唯一的關聯就是需要通過類名來調用這個方法
class Dog(object): def __init__(self,name): self.name = name @staticmethod #把eat方法變為靜態方法 def eat(self): print("%s is eating" % self.name)d = Dog("hahaha")d.eat()
上面的調用會出以下錯誤,說是eat需要一個self參數,但調用時卻沒有傳遞,沒錯,當eat變成靜態方法後,再通過執行個體調用時就不會自動把執行個體本身當作一個參數傳給self了。
Traceback (most recent call last): File "/Users/jieli/PycharmProjects/python_projects/物件導向/靜態方法.py", line 9, in <module> d.eat()TypeError: eat() missing 1 required positional argument: ‘self‘
想讓上面的代碼可以正常工作有兩種辦法
1.調用時主動傳遞執行個體本身給eat方法,即d.eat(d)
class Dog(object): def __init__(self,name): self.name = name @staticmethod #把eat方法變為靜態方法 def eat(self): print("%s is eating" % self.name)d = Dog("hahaha")d.eat(d)運行輸出:hahaha is eating
2.在eat方法中去掉self參數,但這也意味著,在eat中不能通過self.調用執行個體中的其它變數了
class Dog(object): def __init__(self,name): self.name = name @staticmethod def eat(): print(" is eating")d = Dog("hahaha")d.eat()運行輸出: is eating
屬性方法
屬性方法的作用就是通過@property把一個方法變成一個靜態屬性
class Dog(object): def __init__(self,name): self.name = name @property def eat(self): print(" %s is eating" %self.name)d = Dog("hahaha")d.eat()
調用會出以下錯誤, 說NoneType is not callable, 因為eat此時已經變成一個靜態屬性了, 不是方法了, 想調用已經不需要加()號了,直接d.eat就可以了
Traceback (most recent call last): ChenRonghua is eating File "/Users/jieli/PycharmProjects/python_projects/物件導向/屬性方法.py", line 8, in <module> d.eat()TypeError: ‘NoneType‘ object is not callable
正常調用如下
d = Dog("hahaha")d.eat輸出 hahaha is eating
下一篇部落格會涉及@property的詳細情況,這裡只作簡介
另外可以參考部落格:http://www.cnblogs.com/alex3714/articles/5213184.html
繼承1.單繼承
顧名思義,單繼承是指子類只繼承一個父類。
#定義類class People: #定義基本屬性 name = ‘‘ age = 0 #定義私人屬性,私人屬性在類外部無法直接進行訪問 __weight = 0 #定義構造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 說: 我 %d 歲。" %(self.name,self.age))#單繼承class Student(people): grade = ‘‘ def __init__(self,n,a,w,g): #調用父類的構函 People.__init__(self,n,a,w) #或者用super方法 #super(People, self).__init__(n,a,w) self.grade = g #覆寫父類的方法 def speak(self): print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade))s = Student(‘ken‘,10,60,3)s.speak()
2.多繼承
子類同時繼承多個父類
#類定義class People: #定義基本屬性 name = ‘‘ age = 0 #定義私人屬性,私人屬性在類外部無法直接進行訪問 __weight = 0 #定義構造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 說: 我 %d 歲。" %(self.name,self.age)) #單繼承 class Student(people): grade = ‘‘ def __init__(self,n,a,w,g): #調用父類的構函 People.__init__(self,n,a,w) self.grade = g #覆寫父類的方法 def speak(self): print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade))#另一個類,多重繼承之前的準備class Speaker(): topic = ‘‘ name = ‘‘ def __init__(self,n,t): self.name = n self.topic = t def speak(self): print("我叫 %s,我是一個演說家,我演講的主題是 %s"%(self.name,self.topic))#多繼承class Sample(Speaker,Student): a =‘‘ def __init__(self,n,a,w,g,t): Student.__init__(self,n,a,w,g) Speaker.__init__(self,n,t)test = Sample("Tim",25,80,4,"Python")test.speak() #方法名同,預設調用的是在括弧中排前地父類的方法
經典類和新式類
經典類的繼承演算法: 深度優先演算法 新式類的繼承演算法: 廣度優先演算法
python3全部都是新式類
在python2中既有新式類也有經典類
- 在繼承後,通過子類的對象調用某方法,如果子類沒有, 則去父類找, 如果父類也沒有, 就報錯。
- 私人屬性、私人方法, 在類的內部都是可以訪問的, 類的外部或者子類都不可以訪問
多態
Pyhon 很多文法都是支援多態的,比如 len(),sorted()等, 你給len傳字串就返回字串的長度,傳列表就返回列表長度。
關於多態的講解,廖雪峰老師的教程裡講的很容易理解,這裡附上地址:繼承和多態參考文章
另外附上老男孩Python3教程的物件導向講解,因為他最後講到了領域模型:傳送門
最後關於多態,截取廖雪峰老師的一點講解:
靜態語言 vs 動態語言
對於靜態語言(例如Java)來說,如果需要傳入Animal類型,則傳入的對象必須是Animal類型或者它的子類,否則,將無法調用run()方法。
對於Python這樣的動態語言來說,則不一定需要傳入Animal類型。我們只需要保證傳入的對象有一個run()方法就可以了:
Python學習—物件導向學習上