面向對象
關於面向對象的標准定義網上有很多,不再講述,現在我們來通俗點理解:
面向對象編程相對於面向過程編程和函數式編程來說,看的更長遠,實現功能相對更簡單。
面向對象:對象就是物體,這種編程思想就是設定一個有一定功能的物體,然后利用這個物體的功能做你想做的事情。(這個物體有attributes,比如名字啊,年齡啊等等等等,有methods,比如吃喝拉撒睡等等等等,功能==methods)
面向過程:你想干嘛,就直接寫個功能,然后做你想做的事情。
假如你想寫個程序去洗衣店洗衣服,面向對象就是設定一個人,把這個對象賦予拿衣服,搭車,交易,取衣服,回家這所有的過程的功能。當你想洗衣服的時候,創造這個對象的實例出來,然后命令他去做就好了。
面向過程:你就得寫拿衣服,搭車,交易,取衣服,回家這所有的過程。實現這一個功能感覺和面向對象編程的思想差不多,但是如果你下一次還想洗衣服,就得再寫一遍這個過程,假如會有很多次洗衣服,那就得寫更多次,很麻煩,易出錯。
面向過程:你想干嘛,就直接寫個功能,然后做你想做的事情。
假如你想寫個程序去洗衣店洗衣服,面向對象就是設定一個人,把這個對象賦予拿衣服,搭車,交易,取衣服,回家這所有的過程的功能。當你想洗衣服的時候,創造這個對象的實例出來,然后命令他去做就好了。
面向過程:你就得寫拿衣服,搭車,交易,取衣服,回家這所有的過程。實現這一個功能感覺和面向對象編程的思想差不多,但是如果你下一次還想洗衣服,就得再寫一遍這個過程,假如會有很多次洗衣服,那就得寫更多次,很麻煩,易出錯。
定義類,實例化對象
class myclass: # 用關鍵字class進行類的定義 即 “class 類名:” def __init__(self): # 類中定義的函數叫做 “方法” __init__ 方法是構造方法 根據類創建對象時自動執行 # self為實例化的對象本身 即即將被實例化的對象obj print("this is my class") obj = myclass() # 實例化類 創建對象 會自動執行類中的 __init__ 運行結果:this is my class
當然, __init__() 方法可以有參數,參數通過 __init__() 傳遞到類的實例化操作上。例如:
class myclass: def __init__(self, name1, age1): self.name = name1 self.age = age1 # 因為會自動執行類中的 __init__方法,且該方法中有參數,所以實例化對象時需要傳遞參數 # self是一個形式參數, 當執行下句代碼時,實例化對象obj1,那么self就等於obj1這個對象 obj1 = myclass("IKTP", 22) # 當執行下句代碼時,實例化對象obj2,那么self就等於obj2 # 且這兩個對象同都擁有兩個屬性:name,age obj2 = myclass("hahaha", 23) # 當需要調用對象的屬性時,即name和age屬性,可以直接用對象名字后打點調用需要的屬性,例如: print(obj1.name) # 執行結果:IKTP print(obj1.age) # 執行結果:22 print(obj2.name) # 執行結果:hahaha print(obj2.age) # 執行結果:23
方法
方法分為三種:實例方法,類方法,靜態方法
class myclass: public_var = "this is public_var" def __init__(self, name1, age1): self.name = name1 self.age = age1 # 在類里面定義的函數,統稱為方法,方法參數自定義,可在方法中實現相關的操作 # 創建實例方法時,參數必須包括self,即必須有實例化對象才能引用該方法,引用時不需要傳遞self實參 def speak(self): print("this is def speak.%s 說:我今年%d歲。" % (self.name, self.age)) # 我們要寫一個只在類中運行而不在實例中運行的方法. 如果我們想讓方法不在實例中運行 # 比如我們需要類中的基礎字段public_var,根本不需要實例化對象就可以拿到該字段 # 這時就需要裝飾器@classmethod來創建類方法,至於classmethod的使用場合,會在下篇文章介紹 # 創建類方法時,參數必須包括cls,即必須用類來引用該方法,引用時不需要傳遞cls實參 @classmethod def speak2(cls): print("this is classmethod") return cls.public_var # 經常有一些跟類有關系的功能但在運行時又不需要實例和類參與的情況下需要用到靜態方法 # 寫在類里的方法,必須用類來調用(極少數情況下使用,一般都在全局里直接寫函數了) @staticmethod def speak3(name2, age2): print("this is staticmethod.%s 說:我今年%d歲。" % (name2, age2)) obj = myclass("IKTP", 22) # 無論是類方法、靜態方法還是普通方法都可以被實例化的對象調用 # 但是靜態方法和類方法可以不用對象進行調用 obj.speak() # 執行結果:this is def speak.IKTP 說:我今年22歲。 var = obj.speak2() # 執行結果:this is classmethod print(var) # 執行結果: this is public_var obj.speak3("liu", 23) # 執行結果:this is staticmethod.liu 說:我今年23歲。 myclass.speak() # 報錯,實例方法不能直接被調用,必須需要實例化的對象調用 var2 = myclass.speak2() # 不需要實例化對象即可拿到該字段 print(var2) # 不需要實例化對象即可拿到該字段 myclass.speak3("abc", 12) # 不需要實例化對象即可執行
繼承
繼承是面向對象的重要特征之一,繼承是兩個類或者多個類之間的父子關系,子類繼承了父類的所有公有實例變量和方法。
繼承實現了代碼的重用。重用已經存在的數據和行為,減少代碼的重新編寫。
python在類名后用一對圓括號表示繼承關系,括號中的類表示父類
class father: father_var = "this is father_var" def father_def(self): print("this is father_def") class son(father): # 括號里有類名,代表該類是子類(派生類),繼承自父類(基類)father pass obj = son() # son子類中沒有father_var 則去父類father中尋找 print(obj.father_var) # 執行結果:this is father_var # son子類中沒有father_def方法 則去父類father中尋找 obj.father_def() # 執行結果:this is father_def
如果父類中有構造方法且子類也有構造方法,則在子類中必須親自調用且傳遞相對應的參數
# 如果父類中有構造方法且子類也有構造方法,則在子類中必須親自調用且傳遞相對應的參數 # 否則無法找到在父類中定義的屬性 # ##############################錯誤方式################################## class father: def __init__(self, n): self.name = n class son(father): def __init__(self): print("aaaaa") obj = son() # 子類中沒有name屬性,且沒有在子類中調用並傳遞相對應的參數給父類的構造方法,所以找不到name屬性 報錯 print(obj.name) ######################################################################## # #########################正確方式###################################### class father: def __init__(self, n): self.name = n class son(father): def __init__(self): father.__init__(self, "6666") obj = son() print(obj.name) # 執行結果 6666 ########################################################################
如果父類中沒有構造方法 則不必調用
如果父類中有構造方法但子類中沒有構造方法,則在實例化子類對象的時候需要傳遞父類中的構造參數
class father: def __init__(self, n): print("bbbbbbbb") self.name = n class son(father): def speak(self): print("aaaaa") obj = son() # 報錯 因為子類沒有構造方法,所以會執行父類的構造方法,但是沒有給父類的構造方法傳遞參數 obj = son("IKTP") print(obj.name) # 執行結果: IKTP
在子類中調用父類的方法
class father: def speak(self): print("this is father speak") class son(father): def speak2(self): # ######調用父類的speak方法的三種方式######
father.speak(self) # 直接類名后打點調用方法,需要傳遞self super().speak() # super()代指父類 super(son, self).speak()
######################################## print("this is son speak2") obj = son() obj.speak2() # 運行結果:this is father speak # this is father speak # this is father speak # this is son speak2
覆蓋(重寫)父類方法
class father: def speak(self): print("this is father speak") def speak2(self): print("this is father speak2") class son(father): def speak2(self): print("this is son speak2") obj = son() obj.speak2() # 運行結果:this is son speak2 # 對象總會先在實例化該對象的類里尋找屬性字段或者方法, # 如果有則執行,如果該類里沒有,則再去父類里尋找 # 由於父類本來擁有speak2方法,但是子類里又寫了一個speak2方法 # 所以obj不會去父類里尋找該方法,只會執行子類里的speak2方法,這樣就稱為覆蓋或重寫
多繼承
一個類可同時繼承多個類,與多個類具有繼承關系,則這個類可調用所有父類中的方法和字段
class father1(): father1_var = "this is father1_var" def father1_def(self): print("this is father1_def") class father2(): father2_var = "this is father2_var" def father2_def(self): print("this is father2_def") class son(father1, father2): # son類同時繼承father1類和father2類 def son_def(self): print("this is son_def") obj = son() print(obj.father1_var) print(obj.father2_var) obj.father1_def() obj.father2_def() # 執行結果: # this is father1_var # this is father2_var # this is father1_def # this is father2_def
那么問題來了,假如父類中有相同的字段或者方法名,那么會調用誰的呢?我們來試一試
myclass類同時繼承son類和son2類,son類繼承father類,father類和son2類又同時繼承grandf類
並且每個類里面都有speak方法,我們實例化myclass對象調用speak方法去試試
class grandfather: def speak(self): print("this is grandfather_def") class father(grandfather): def speak(self): print("this is father_def") class son(father): def speak(self): print("this is son_def") class son2(grandfather): def speak(self): print("this is son2_def") class myclass(son, son2): def speak(self): print("this is myclass_def") obj = myclass() obj.speak() # 執行結果:this is myclass_def
#先執行myclass里的speak方法
將myclass里的方法speak方法注釋掉再運行
class grandfather: def speak(self): print("this is grandfather_def") class father(grandfather): def speak(self): print("this is father_def") class son(father): def speak(self): print("this is son_def") class son2(grandfather): def speak(self): print("this is son2_def") class myclass(son, son2): pass obj = myclass() obj.speak() # 執行結果:this is son_def
#執行的是son類里的方法
將myclass、son類里的方法speak方法注釋掉再運行
class grandfather: def speak(self): print("this is grandfather_def") class father(grandfather): def speak(self): print("this is father_def") class son(father): pass class son2(grandfather): def speak(self): print("this is son2_def") class myclass(son, son2): pass obj = myclass() obj.speak() # 執行結果:this is father_def
#執行的是father類的方法
將myclass、son、father類里的方法speak方法注釋掉再運行
class grandfather: def speak(self): print("this is grandfather_def") class father(grandfather): pass class son(father): pass class son2(grandfather): def speak(self): print("this is son2_def") class myclass(son, son2): pass obj = myclass() obj.speak() # 執行結果:this is son2_def
#執行的是son2類里的方法
將myclass、son、father、son2類里的方法speak方法注釋掉再運行
class grandfather: def speak(self): print("this is grandfather_def") class father(grandfather): pass class son(father): pass class son2(grandfather): pass class myclass(son, son2): pass obj = myclass() obj.speak() # 執行結果:this is grandfather_def
#執行的方法是grandfather類的方法
由上可知,myclass實例化的對象找speak方法的執行順序是 myclass-->son-->father-->son2-->grandfather
所以得出結論:經典類的搜索方式是按照“從左至右,深度優先”的方式去查找屬性,有共同父類的話最后查找父類,無共同父類則“從左到右”挨個將左邊類的所有父類關系查找一遍,再向右查找