一、主題: 面向對象 二、目的: 歸納總結,將之前筆記轉化為自己能夠記住並理解的東西 三、大綱 1、概述 2、詳述 四、內容: 概述 面向對象在很多語言中都會涉及,各種百科給出的答案也是十分詳細,但站在當前學習的初級階段,在此概述時,僅涉及面向對象三大基本特性的內容,即:封裝、繼承、多態。 封裝就是 ...
一、主題:
面向對象
二、目的:
歸納總結,將之前筆記轉化為自己能夠記住並理解的東西
三、大綱
1、概述
2、詳述
四、內容:
概述
面向對象在很多語言中都會涉及,各種百科給出的答案也是十分詳細,但站在當前學習的初級階段,在此概述時,僅涉及面向對象三大基本特性的內容,即:封裝、繼承、多態。
封裝就是把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
繼承是指這樣的一種能力:它可以使用自己當前現有的所有功能,並且在無需編寫原來的類的情況下對這些功能進行擴展。
多態是指一個類實例的相同方法在不同情形下有不同表現形式,多態機制使不同內部結構的對象可以共用相同的外部介面。這意味著,雖然針對不同對象的具體操作不同,但通過一個公共的類,他們可以通過相同的方式予以調用。
詳述
一、封裝
封裝是做什麼的前面已經提及,在這裡主要說的是為什麼要封裝以及如何來封裝。
1、 為什麼要封裝
在說明為什麼要封裝之前,首先要知道什麼才需要進行封裝,即封裝的主體要確定。確定主體最簡單的方式就是反推,現在拿一個類來進行分析:
# -*- coding:utf-8 -*- class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talking(self): print("我叫%s,今年%s歲,我是%s生"%(self.name,self.age,self.gender))
只是從之前的知識上來看,上述代碼中存在了數據name,age,gender和函數talking,再觀察其他的類會發現,存在的也都為此兩類,最多只是形式上不同,不會牽扯進其他新的內容。
那麼我們就能夠瞭解到,類中存在數據和函數兩種情況。
對於數據的封裝,考慮最多的還是安全性,是為了更好的保護隱私。
對於函數的封裝,隔離複雜度。舉個例子的話就是我們在使用各個系統時,不需要知道其內部是怎麼運行的,只需要知道在需要某個功能時,將對應的選擇框勾選上即可。
PS.對於函數的封裝,多數提到的是方法的封裝。其實,方法即為類中定義的函數。函數和方法的封裝可參考此處說明http://blog.csdn.net/lastinglate/article/details/7295248
2、怎麼進行封裝
上述提到的代碼其實就已經完成了一個最簡單的封裝。如果在不考慮面向對象的前提下,封裝就是將數據和函數放入類下即可。比如:
class Fangzi(): def __init__(self,weizhi,jiage,mianji): self.weizhi=weizhi self.__jiage=jiage self.mianji=mianji def shuoming(self): ''' 方法主要是對此套房子進行說明 :return: 無返回值 ''' print("這套房子位於%s,單價是%s元"%(self.weizhi,self.__jiage)) def maidiao(self): ''' 方法時用來計算當前房子的總價並返回其值 :return: 房子的總價 ''' self.zongjia=int(self.__jiage)*int(self.mianji) return self.zongjia def swimming(self): ''' 此方法沒有啥用 :return: ''' print('小明會游泳')
簡單介紹下代碼內容:
在此代碼中,class為創建類的關鍵詞,表示創建的此項內容為類。
def __init__(self,weizhi,jiage,mianji)為構造函數,或者叫構造方法。代表的是傳入的參數。 self指的是實例本身,也可以理解為傳入參數後形成的對象的內容,他的作用主要是起到連通類內部,使得類內多個方法都需要的值在self中可以隨時調用到。 self.weizhi=weizhi是將傳入的weizhi賦值給了self.weizhi,保證了在類內部的通行。 self.__weizhi代表了私有屬性,表示外部無法訪問到此屬性。不過外部在某些時候可能也需要在允許的情況對私有屬性進行訪問,那麼這時候可以使用如下形式:P=Fangzi("1",10000,200) shuxingming=P._Fangzi__jiage #具體格式為 實例化對象._(一個橫線)類名__(這兩個橫線一般為私有屬性前面的那個橫線)屬性名 print(shuxingming)
另一方面,加上面向函數概念的話,那麼類就需要做到包含某一客觀對象存在的共性。因此,在封裝成類的時候,就需要考慮封裝的數據和方法是否與成類的函數存在關聯。比如上述函數,不應該將swimming方法封裝到Fangzi這個類中,畢竟房子與游泳完全沒有聯繫,而且後續使用過程中也不會牽扯到相關方面。這樣抽象成的類不符合面向對象思想,甚至還會影響到正常的邏輯思維,嚴重的會幹擾後續系統設計的思路,完全把自己帶進溝里。
3、特殊的封裝
前面說明瞭如何進行封裝,也介紹了進行封裝的形式,基本上類可以用這類形式直接構造出來。但事物總是複雜的,用來描述事物的類自然也需要適應這種複雜。那麼除了這種普通的封裝形式,自然要延伸出特殊的封裝形式。
特殊的封裝形式在python中主要包含了以下幾種:
1)、classmethod 類方法
方式:在類前使用關鍵詞@classmethod,將方法更改為類方法。
與普通類的不同點:類方法只能使用全局下的屬性,而不能使用構造方法中的self.屬性
舉例:
class Person(): name="li" def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender #若在前加@classmethod,則只能使用全局下的name,而不能使用構造函數中的self.name @classmethod def talk(self): print("%s is talking"%self.name)
調用以上代碼
d=Person("lu",26,"male") d.talk()
結果為:
若要測試更改為類方法後,是否還可以調用構造方法中的值,可以取一個全局沒有的值,比如age
@classmethod def talk(self): print("%s is talking"%self.age)
運行結果為:
在以上測試中,類方法在括弧內的值可以改為cls,不用預設的self。這樣可以區分類方法與其餘方法的不同,類方法在使用關鍵詞classmethod後,自身變為類方法,而類方法預設的第一個參數變為類本身(此處可以理解為就是類這個代碼自身),而不是self代表的實例本身。此處仍舊使用self是為了證實第二步調用self.name報錯的直觀性,不至於誤會是因為沒有調用self內的name而導致的出錯。
2)、property 屬性方法
方式: 在類前面使用關鍵詞@property,將方法改為屬性方法
與普通方法的不同:在將方法變為一個屬性方法後,調用時就不需要加括弧了。 直接調用即可。
調用時不使用括弧這種形式正跟屬性調用類似,而且配合setter裝飾器,可以有效的控制重要函數值的傳參。
舉例:
class Person(): name="li" def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender @property def talk(self): print("%s is talking"%self.name)
#調用如下: d= Person("liu",26,"male") d.talk
現在結合setter裝飾器一塊說明:
class Person(): @property def description(self): aihao="打麻將" return aihao #調用如下: d=Person() print(d.description)
此結果很明顯,會直接輸出打麻將
但下麵這些代碼,嘗試修改descrption的值:
class Person(): @property def description(self): aihao="打麻將" return aihao #調用如下: d=Person() d.description="游泳" print(d.description)
就會發現直接出現報錯,提示不能設置屬性
若要設置description的屬性,則需要
class Person(): #設定登錄名是從認證函數中調取出來的,不允許私自更改。 @property def description(self): self.__username="wanglei" return self.__username @description.setter def decs(self,value): self.__username=value return self.__username #調用如下: d=Person() d.description="zhangsan" print(d.description)
3)、staticmethod 靜態方法
方式: 在類前面使用關鍵詞@staticmethod,將方法改為靜態方法
與普通方法的不同:這樣寫完以後,方法實際上跟類沒什麼關係了。相當於跟類的關係截斷了。只是名義上歸類管理,實際上在靜態方法里訪問不了實例中的屬性,只能獲取到類中的屬性。
舉例:
class Person(): #設定登錄名是從認證函數中調取出來的,不允許私自更改。 user="laowang" def test(self): self.username="laoli" @staticmethod def statictest(self): print(self.username,",Hello") #調用如下: d=Person() d.statictest(d)
此處在創建函數時仍舊調用了self,但經過測試,即使此處是self,也是無法調用self下的其他值。這樣使用只會出現報錯,提示無法找到屬性username:
如下調用即可正常顯示:
class Person(): #設定登錄名是從認證函數中調取出來的,不允許私自更改。 user="laowang" @staticmethod def statictest(self): print(self.user,",Hello") #調用如下: d=Person() d.statictest(d)
結果為
4、特殊成員方法
類中除了將方法使用特殊形式封裝外,還提供了特殊的成員方法,其形式主要是以為 __方法名__ 這種形式存在,現在主要是介紹下此類特殊成員方法
1)__doc__
作用: 列印這個類的描述信息
示例:
class Person(): __doc__ = "存儲了人的行為" def __init__(self,name): self.name=name def damajiang(self): print("%s在打麻將"%self.name) def dushu(self): print("%s在讀書"%self.name) d=Person("zhangsan") print(d.__doc__)
2)、__moudile__
作用:表示當前操作的對象在哪個模塊中
示例:使用之前寫過的一個作業來進行說明。首先開始程式是在一個文件夾下,調用的核心程式是在另一個文件夾下的classset.py文件內,使用__module__就可以直接看到調用了哪個模塊的內容。
while True:#開啟程式的界面 msg=[] print(display) select=input("請選擇:").strip() if select=="1" or select=="管理界面": msg.append("administrator") msg.append(None) adminmgr=AdminMgr(msg) print(adminmgr.__module__) adminmgr.maincore(msg)
#核心程式界面 @denglu def maincore(self): print("歡迎進入選課系統管理界面".center(50,"-")) while True: msg=''' 1.創建校區 2.創建講師 3.創建課程 4.創建班級 5.查看在職講師 6.查看在校學生 7.查看開課課程 8.退出 ''' print(msg) select=input("請選擇:").strip()
最終得到的結果為:
3)、__class__
作用:表示當前操作的對象的類是什麼
示例:
正好可以拿上面的示例來做對比,看下__module__跟__class__得出的結果是否存在不同
while True: msg=[] print(display) select=input("請選擇:").strip() if select=="1" or select=="管理界面": msg.append("administrator") msg.append(None) adminmgr=AdminMgr(msg) #將此處代碼改為__class__ print(adminmgr.__class__) adminmgr.maincore(msg)
得到的結果為:
可以看出__class__的結果已經精確到了AdminMgr這個類上了。
4)、__init__
作用:構造函數,也成為構造方法
前面已經講完,不再重覆敘述。
5)、__del__ 析構函數 或者是析構方法
作用:當對象在記憶體中被釋放時,自動觸發執行。
示例:
可用文字描述下使用場景:比如軟體是以server和client的模式存在,當前兩者都正常運行,現在server端想要停機維護,如果直接停機的話,可能會引起當前仍舊線上的客戶端傳輸出現問題,那麼此時在server在退出時,可以設置析構函數,告訴server在退出時,發送斷開信息給客戶端,然後再將所有客戶端連接斷開,這樣客戶端在收到信息後,可執行自身無法連接至伺服器的流程,消除了兩者都可能引起崩潰的情況。
6)、__call__
作用:對象後面加擴展,直接執行
示例:
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("%s is talking"%self.name) def __call__(self, *args, **kwargs): print("this is %s call"%self.name) d=Person("liu",26,"male") d()
7)、__dict__
作用:查看對象中所有的成員
PS.類.__dict__ 列印類里的所有屬性,不包括實例屬性
實例.__dict__列印所有實例屬性,不包括類里的屬性
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("talking") d=Person("liu",26,"male") print(d.__dict__) print(Person.__dict__)
結果為:
8)、__str__
作用:列印對象時,會直接將__str__內的返回值列印出來。
示例:
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("talking") def __str__(self): return "123" d=Person("liu",26,"male") print(d)
9)、__getitem__
作用:用於索引操作,表示獲取數據
示例:
class Value(): def __init__(self): self.dict={ 1:"A", 2:"B", 3:"C", 4:"D" } def __getitem__(self, item): if item in self.dict.keys(): return self.dict[item] s=Value() print(s[1])
10)、__setitem__
作用:用於索引操作,表示設置數據
示例:
# -*- coding:utf-8 -*- class Value(): def __init__(self): self.dict={ 1:"A", 2:"B", 3:"C", 4:"D" } def valuestr(self): return self.dict def __setitem__(self, key, value): self.dict[key]=value def __getitem__(self, item): if item in self.dict.keys(): return self.dict[item] s=Value() s[1]="qwe" print(s[1]) print(s.valuestr())
最後的結果為:
11)、__delitem__
作用:刪除給定鍵對應的元素。
示例:
class Value(): def __init__(self): self.dict={ 1:"A", 2:"B", 3:"C", 4:"D" } def valuestr(self): return self.dict def __setitem__(self, key, value): self.dict[key]=value def __getitem__(self, item): if item in self.dict.keys(): return self.dict[item] def __delitem__(self, key): del self.dict[key] s=Value() s[1]="qwe" del s[2] print(s.valuestr())
最終結果為:
12)、__iter__
作用:用於迭代器,之所以列表、字典、元組可以進行for迴圈,是因為類型內部定義了 __iter__
13)、__new__
作用:是在新式類中出現的方法,它作用在構造方法建造實例之前.
__new__()可以決定是否 要使用當前類__init__()方法,因為__new__()同樣也可以調用其他類的構造方法或者直接返回別的對象來作為本類 的實例。
從繼承的角度來說,如果類在定義時沒有重寫__new__()方法,Python預設調用該類的父類__new__()方法來構造該類實例,若父類中也未進行重寫,那麼就一直追溯至object的__new__()方法,因為object是所有新式類的基類。
而如果新式類中重寫了__new__()方法,那麼可以在重寫__new__()方法後,調用任意新式類的__new__()方法來進行實例的創造。 14)、__metaclass__ 作用:__metaclass__稱為“元類”,元類就是用來創建類的東西。 可以這樣簡單理解:MyClass = MetaClass()
MyObject = MyClass()
在此還要提到type創建類的使用方法:
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("talking") d=Person("liu",26,"male") print(type(d)) print(type(Person))
在以上函數中,d通過Person成為實例化對象,在此,d跟Person其實都是一個實例。通過列印d 和Person的類型來看,二者都是通過類來創建的。
def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print(" %s is talking"%self.name) def eat(self): print("%s is eat, he/she is %s"%(self.name,self.age)) Person=type("Person",(object,),{"talk":talk,"__init__":__init__}) d=Person("liu",25,"male") d.talk()
這兩者創建的類最終結果都一樣,因此也就瞭解到了,type實際上就是一個元類,type就是Python在背後用來創建所有類的元類。
繼續說明的話希望藉助此處的一篇文章來進行進一步的說明。http://www.maiziedu.com/article/27320/
二、繼承
繼承的模式如下示例:
class A(): def __init__(self,value): self.value=value def output(self): print("A") class B(A): def __init__(self,value,value1): super(B, self).__init__(value) self.value1=value1 def output1(self): print("B") s=B("1","2") s.output() s.output1()
正如其說明,在B原有的功能上,添加了A中的功能,使得由B創建的對象可以調用A中的output方法。
在這裡,需要說明的是__init__中的super關鍵詞。
提到super關鍵詞,就需要說明新式類和經典類。python2.X中,預設採用的是經典類,只有顯式繼承了object的才是新式類。python3.X中,預設採用的都是新式類,不必顯式的繼承object。
顯示繼承如下:
class A(object): def __init__(self,value): self.value=value def output(self): print("A")
而super關鍵詞只是用在新式類中的寫法,經典類中是使用另外一種來進行書寫,寫法如下:
A.__init__(self,value)
除了super關鍵詞使用不同以外,還有一個不同點是在繼承關係上。
新式類在進行搜索時候,使用的是廣度優先搜索,經典類使用的是深度優先搜索。
那下麵的例子來進行說明:
class A(): def output(self): print("A") class B(A): def output1(self): print("B") class C(A): def output2(self): print("C") class D(B,C): def output3(self): print("D") s=D()
使用類D創建一個對象,然後調用方法output
那麼新式類的搜索方式是在D里先找,找不到的話去B裡面找,然後再去C,最後去A。
經典類的順序則是D---B--A--C。即:“從左至右,深度查找”
三、多態
多態的實例如下:
class Person(): def __init__(self,name,age,gender): self.name=name self.age=age self.gender=gender def talk(self): print("%s is talking"%self.name) class Child(Person): def __init__(self,name,age,gender): super(Child, self).__init__(name,age,gender) def talk(self): print("%s is child"%self.name) class Adult(Person): def __init__(self,name,age,gender): super(Adult, self).__init__(name,age,gender) def talk(self): print("%s is adult"%self.name) #此處定義的函數,是獨立於類之外的,即為了實現多態,重新定義了一個新的類,讓其能夠滿足一個介面,多種形態。 def talk(obj): obj.talk() d=Child("liu",10,"male") e=Adult("lu",26,"male") talk(d)
結果為:
五、總結
整篇筆記是以面向對象的三大特性為骨架進行填充整理的,基礎部分在此多數都已涉及,但此篇歸檔在__metaclass__和__iter__兩個類上尚且存在疑問,後續也會針對相關內容進行查缺補漏。