小白學習之路,初識面向對象
一,編程範式
所謂編程範式(programming paradigm),指的是計算機編程的基本風格或典範模式。怎麽說呢,每個人都有自己不同的習慣,當然編程也是一樣的,每個程序組員根據自己不同的習慣會寫出不同的代碼。當然這樣肯定是不行的啦,這樣的可讀性不強,而且太亂了,所以慢慢慢慢的大家就統一編程的風格。編程範式裏面呢又包括下面幾種(我知道的):面向對象,面向過程,函數式編程。感覺寫到這裏,需要用一波我自己的土話來跟大家解釋一下這三種的大概不同了。
面向對象:在python裏面一切皆對象,當然不是你女(男)朋友那個對象。emmm,打個比方吧,假如你高考完了,準備選學校,選專業了,你在想到底是去北大呢,還是去清華呢。你就想可以寫個函數,如果選清華有哪些專業,選北大有哪些專業。當然還可以這樣寫,你可以把清華看成一個對象,北大看成一個對象,然後裏面的對象有不同的屬性跟方法。你選擇了清華,就是實例化了一個清華對象,下一個人選擇北大,就實例化一個北大對象。這樣看似沒得多大區別,但是他能夠在裏面的屬性跟方法添加其他的,可擴展性更強了,而且容易修改,這種思想其實就是面向對象。
面向過程:面向過程就太好理解了。因為我們這種小白,最熟悉的就是面向過程。面向過程呢,就是一步一步實現,然後到最後完成。面向過程就是重要的是過程,但是在使用面向過程時,你想改其中一個地方,你會發現很多地方都要改,一個地方錯了,其他地方也錯了,所以很麻煩。
函數式編程:其實,我覺得跟面向過程差不多,然後是用函數實現的一種編程方式。
二,面向對象簡介
1,認識面向對象
- 類(Class): 用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。
- 類變量:類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體之外。類變量通常不作為實例變量使用。
- 數據成員:類變量或者實例變量, 用於處理類及其實例對象的相關的數據。
- 方法重寫:如果從父類繼承的方法不能滿足子類的需求,可以對其進行改寫,這個過程叫方法的覆蓋(override),也稱為方法的重寫。
- 實例變量:定義在方法中的變量,只作用於當前實例的類。
- 繼承:即一個派生類(derived class)繼承基類(base class)的字段和方法。繼承也允許把一個派生類的對象作為一個基類對象對待。例如,有這樣一個設計:一個Dog類型的對象派生自Animal類,這是模擬"是一個(is-a)"關系(例圖,Dog是一個Animal)。
- 實例化:創建一個類的實例,類的具體對象。
- 方法:類中定義的函數。
- 對象:通過類定義的數據結構實例。對象包括兩個數據成員(類變量和實例變量)和方法。
2,面向對象特性
面向對象有三大特性:封裝,繼承,多態
封裝:把客觀事物封裝成抽象的類,並且類可以把自己的數據和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。
繼承:繼承是指這樣一種能力:它可以使用現有類的所有功能,並在無需重新編寫原來的類的情況下對這些功能進行擴展。通過繼承創建的新類稱為“子類”或“派生類”。被繼承的類稱為“基類”、“父類”或“超類”。在這裏值得提一下的就是,python支持多繼承,之後多繼承後面也會提到。通俗來講,繼承就是你繼承其他的類,他的屬性方法,你都有,而且還可以繼續增加或者修改一些屬性或者方法。
多態:多態性(polymorphisn)是允許你將父對象設置成為和一個或更多的他的子對象相等的技術,賦值之後,父對象就可以根據當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。
封裝可以隱藏實現細節,使得代碼模塊化;繼承可以擴展已存在的代碼模塊(類);它們的目的都是為了——代碼重用。而多態則是為了實現另一個目的——接口重用!多態的作用,就是為了類在繼承和派生的時候,保證使用“家譜”中任一類的實例的某一屬性時的正確調用。
三,面向對象基礎語法
聽了這麽多的類啊,對象啊,現在就來新建一個類吧。在新建之前,類還分經典類和新式類。
經典類:
1 import time 2 class people:#(class後面的是類名) 3 #構造函數,在實例化一個對象的時候生成的一些屬性寫在這裏 4 def __init__(self,name): 5 self.name=name 6 def eat(self): 7 print(‘%s is eating...‘%self.name) 8 def sleep(self,times): 9 print(‘%s Is sleeping‘%self.name) 10 time.sleep(times) 11 print(‘%s sleep up‘ %self.name) 12 a=people(‘zzq‘) #實例化一個對象 13 a.eat() #調用裏面的方法 14 a.sleep(1)#調用需要傳參的方法View Code
新式類:(繼承了object)
1 import time 2 class people(object):#(class後面的是類名) 3 #構造函數,在實例化一個對象的時候生成的一些屬性寫在這裏 4 def __init__(self,name): 5 self.name=name 6 def eat(self): 7 print(‘%s is eating...‘%self.name) 8 def sleep(self,times): 9 print(‘%s Is sleeping‘%self.name) 10 time.sleep(times) 11 print(‘%s sleep up‘ %self.name) 12 a=people(‘zzq‘) #實例化一個對象 13 a.eat() #調用裏面的方法 14 a.sleep(1)#調用需要傳參的方法View Code
他們之間的區別在講了後面的繼承跟多繼承會講到,其實在python3中沒得區別,一般常用新式類的寫法哦。
四,類的繼承
面向對象的編程帶來的主要好處之一是代碼的重用,實現這種重用的方法之一是通過繼承機制。
通過繼承創建的新類稱為子類或派生類,被繼承的類稱為基類、父類或超類。
在python中繼承中的一些特點:
- 1、如果在子類中需要父類的構造方法就需要顯示的調用父類的構造方法,或者不重寫父類的構造方法。詳細說明可查看:python 子類繼承父類構造函數說明。
- 2、在調用基類的方法時,需要加上基類的類名前綴,且需要帶上 self 參數變量。區別在於類中調用普通函數時並不需要帶上 self 參數
- 3、Python 總是首先查找對應類型的方法,如果它不能在派生類中找到對應的方法,它才開始到基類中逐個查找。(先在本類中查找調用的方法,找不到才去基類中找)。
1,下面簡單介紹一下,繼承的一些基本語句
1 class Father(object): 2 def __init__(self,name): 3 self.name=name 4 def get_name(self): 5 print(‘Father’s name is %s‘ %self.name) 6 def say(self): 7 print(‘Father is say‘) 8 class Son(Father):#繼承Father類 9 def __init__(self,name,age): 10 Father.__init__(self,name)#繼承父類的構造方法 11 self.age=age 12 def get_name(self): 13 Father.get_name(self)#繼承父類的方法 14 print(‘Son’s name is %s age is %s‘%(self.name,self.age)) 15 def say(self): 16 print(‘Son is say‘)#重寫say方法 17 a=Son(‘xxx‘,‘18‘) 18 a.get_name() 19 a.say()View Code
執行結果:
在上面的構造方法的繼承其實還有一種比較常用的,也是推薦使用的方法,代碼如下
1 def __init__(self,name,age): 2 # Father.__init__(self,name)#繼承父類的構造方法 3 super(Son, self).__init__(name)#繼承父類的構造方法,並且不用知道父類的名字 4 self.age=age
總結一下吧。如果不重構的情況下,默認,子類有父類所有的方法並且跟父類相同。子類還可以繼承父類的構造方法或者其他方法,並且在上面添加擴展。如果父類跟子類的方法或者屬性重名,那麽使用的還是子類的屬性或方法。
2,多繼承的用法跟分析
在講到多繼承這裏大家就要知道在繼承的時候需要考慮繼承的順序問題。講這麽多不好理解,我們先看代碼,然後在分析吧。
1 class F1(object): 2 def say1(self): 3 print(‘this is F1 say1‘) 4 def say2(self): 5 print(‘this is F1 say2‘) 6 class F2(object): 7 def say1(self): 8 print(‘this is F2 say1‘) 9 def say2(self): 10 print(‘this is F2 say2‘) 11 def say3(self): 12 print(‘this is F2 say3‘) 13 class Son(F1,F2):#繼承F1,F2兩個類 14 pass 15 a=Son() 16 a.say1() 17 a.say2() 18 a.say3()View Code
執行結果:
1 this is F1 say1 2 this is F1 say2 3 this is F2 say3
通過結果我們可以看到,如果同時繼承了兩個類,會首先去前面一個類裏面找有木有方法或者屬性,如果有,就用前面那個類的屬性或方法,如果前面沒有就用後面的那個類的屬性或方法。
講到這裏跟大家講一下經典類跟新式類的區別吧。但是在講區別之前,需要帶大家了解一下什麽叫廣度優先跟深度優先。
廣度優先:從初始點出發,把所有可能的路徑都走一遍,如果裏面沒有目標位置,則嘗試把所有兩步能夠到的位置都走一遍,看有沒有目標位置;如果還不行,則嘗試所有三步可以到的位置。這種方法,一定可以找到一條最短路徑,但需要記憶的內容實在很多,要量力而行。
深度優先:從初始點出發,不斷向前走,如果碰到死路了,就往回走一步,嘗試另一條路,直到發現了目標位置。這種不撞南墻不回頭的方法,即使成功也不一定找到一條好路,但好處是需要記住的位置比較少。
python2經典類是按照深度優先來繼承類的,新式類是按照廣度優先來繼承類的。
python3經典類和新式類都是統一按照廣度優先來繼承類的。
當然,有的人還是不能完全理解繼承流程,下面是我畫的流程圖,當然,醜不能怪我!!!
這個順序是在繼承的時候查找屬性和方法的向上查找順序。
五,類屬性,方法擴展
想了一下,屬性好像有實例變量,類變量,私有變量等。方法有構造方法,析構函數,私有方法,靜態方法,類方法,屬性方法等。
1,私有變量,私有方法
私有屬性跟私有方法,其實就是只能在類裏面能夠使用的屬性跟方法,外部不能調用使用。
1 class tset(object): 2 def __init__(self,name): 3 self.__name=name #變為私有屬性,只有內部能夠調用 4 def __say(self): #變為私有方法,只有內部能夠調用 5 print(self.__name) #調用私有屬性 6 def say(self): 7 self.__say() #調用私有方法
2,靜態方法
普通的方法,可以在實例化後直接調用,並且在方法裏可以通過self.調用實例變量或類變量,但靜態方法是不可以訪問實例變量或類變量的,一個不能訪問實例變量和類變量的方法,其實相當於跟類本身已經沒什麽關系了,它與類唯一的關聯就是需要通過類名來調用這個方法。
1 class Test(object): 2 def __init__(self, name): 3 self.name = name 4 @staticmethod # 把say方法變為靜態方法 5 def say():#不能傳入其他參數 6 print("hello") 7 d = Test("xxx") 8 d.say()
3,類方法
類方法和普通方法的區別是, 類方法只能訪問類變量,不能訪問實例變量
1 class Test(object): 2 name=‘zzq‘ #類變量 3 def __init__(self, age): 4 self.age = age 5 @classmethod # 把say方法變為類方法 6 def say(self): 7 print("hello %s" %self.name)#只能調用類變量 8 d = Test("xxx") 9 d.say()
4,屬性方法
屬性方法的作用就是通過@property把一個方法變成一個靜態屬性
1 class Test(object): 2 def __init__(self, name): 3 self.name = name 4 @property # 把say方法變為屬性方法 5 def say(self): 6 print("hello %s" %self.name) 7 d = Test("xxx") 8 d.say #相當於say是一個屬性,調用的時候相當於調用一個屬性,不用加括號
六,類裏面的一些特殊方法
1,__str__
1 def __str__(self):#輸出對象時,輸出的是return的返回值 2 return ‘hahaha‘
2,__call__
1 def __call__(self, *args, **kwargs):#在調用後面加一個()執行這個方法 2 print(‘this is call‘,args,kwargs) 3 Test("xxx")() #在調用的時候後面再加一個括號
3,__doc__,__module__
1 print(Test.__doc__)#打印類的描述 2 print(Test.__module__)#輸出類所在哪個模塊
4,__class__
print(Test.__class__)#表示當前操作的對象的類是什麽
5,__dict__
1 print(Test.__dict__)#打印類中的所有屬性,不包括實例化屬性 2 a=Test("xxxx") 3 print(a.__dict__)#打印類中所有實例化屬性,不包括類屬性
6,__del__
1 def __del__(self):#析構函數,在實例化以後自動調用,比如一些關閉數據庫連接等操作 2 print(‘end‘)
7,__new__
類的生成 調用 順序依次是 __new__ --> __init__ --> __call__,所以在實例化__init__之前其實先調用了__new__方法的。
七,反射
相信在寫程序的時候都會遇到一個問題就是你想要把字符串轉換成需要執行的屬性或者類。需要執行對象裏的某個方法,或需要調用對象中的某個變量,但是由於種種原因我們無法確定這個方法或變量是否存在,這是我們需要用一個特殊的方法或機制要訪問和操作這個未知的方法或變量,這中機制就稱之為反射。
簡單來講,反射就是把字符串轉化為可以執行的屬性或者方法。
1 class Dog(object): 2 def __init__(self,name): 3 self.name=name 4 def eat(self): 5 print(‘The dog %s is eating...‘ %self.name) 6 d=Dog("xxx") 7 chose=input(">>>>") 8 print(hasattr(d,chose)) #判斷輸入的chose在d對象中,是否存在屬性或者方法 9 10 func=getattr(d,chose) #把字符串轉化為可以直接使用的屬性或者方法 11 func() #如果是方法,調用方法(執行結果:The dog xxx is eating...) 12 print(func) #如果是屬性(執行結果:xxx) 13 14 setattr(d,‘age‘,20)#為d增加屬性,第二個為屬性名,第三個為屬性值 15 print(d.age) #執行結果:20 16 def say(self): 17 print(‘hello,world‘) 18 setattr(d,‘talk‘,say)#把函數say添加到d中,並且函數名為talk 19 d.talk(d) #調用talk方法,因為這是額外添加的方法,需手動傳入對象(執行結果:hello,world) 20 21 delattr(d,‘name‘)#刪除d中的name屬性,delattr不能刪除類中的方法View Code
當然,一般hasattr跟getattr是一起使用的,先用hasattr判斷類或對象中是否存在,然後再用getattr獲取。getattr不僅僅可以獲取對象的方法和屬性和方法,還可以獲取類的方法和屬性。你還可以這樣理解,比如d.name在使用getattr的時候可以理解為getattr(obj,str)obj相當於.前面的,str相當於.後面的。
小白學習之路,初識面向對象