終于是來到了python的面向對象程式設計,以前是沒有接觸過其它的面向對象程式設計的語言,是以學習這一部分是相當帶勁的,這裡也總結一下。
1.面向對象程式設計的相關名詞及解釋
世界萬物,皆可分類,一切皆為對象。
所謂的面向對象程式設計,指的是一種程式設計的思想,通過對具體代碼實作過程(面向過程程式設計)的不斷抽象,以形成一個個的類别,以提高我們進行大型程式編寫的效率(面向對象的具體實作需要面向過程,大型程式也可以用面向過程來編寫,隻是比較麻煩)。對于面向對象程式設計的相關名詞和解釋如下:
對象 :類的實體\一個叫rain的好色的男人
類:人\動物\機器
方法:人會走,會思考\狗會叫,會咬人\定義一個類的各個功能
消息傳遞:狗叫了,人聽見了,就叫通信
繼承:狗都四條腿走路
封裝:人不能引用狗的特性,比如四條腿走路
多态性:一個叫的功能,可能是低吼,也可是大聲叫
抽象性:簡化複雜的現實問題的途徑,它可以為具體問題找到最恰當的類定義
對“類”這個名詞舉個簡單的例子:
杯子:類
這個杯子:對象(實體),屬于“杯子”這個類
對“方法”這個名詞舉個例子:動态方法(動态屬性)與靜态方法(靜态方法可以了解為類的屬性)。
2.類、對象、方法
關于三者的關系,用下面的一張圖檔可以非常清晰明确的說明:
3.類的文法:第一個面向對象程式
1
2
3
4
5
6
7
8
9
10
11
<code>程式代碼如下:</code>
<code>class</code> <code>dog: #定義一個類</code>
<code> </code><code>def name(self): #定義類的方法</code>
<code> </code><code>print </code><code>"hello master, my name is python."</code>
<code>d = dog() #類的執行個體化,産生類的對象,如果不執行個體化,将無法通路該類</code>
<code>d.name() #使用類的方法</code>
<code>執行情況如下:</code>
<code>xpleaf@xpleaf-machine:/mnt/hgfs/python/day4$ python myclass4.py </code>
<code>hello master, my name </code><code>is</code> <code>python.</code>
4.類的方法:有關self
類的方法(類函數)與普通的函數隻有一個特别的差別——它們必須有一個額外的第一個參數名稱,但是在調用這個方法的時候你不為這個參數指派,python會提供這個值。這個特别的變量指對象本身,按照慣例它的名稱是self。
如你有一個類稱為myclass和這個類的一個執行個體myobject。當你調用這個對象的方法myobject.method(arg1, arg2)的時候,這會由python自動轉為myclass.method(myobject, arg1, arg2)。
類中如果有多個不同的方法,并且類中的變量需要互相通路,這時候就需要使用self即用類自身來作為中間變量以進行變量在不同類中的傳遞了,可以看下面的一個例子:
12
13
14
15
16
17
18
19
20
21
22
<code>class</code> <code>dog:</code>
<code> </code><code>class_object = </code><code>'food'</code> <code>#這是一個類變量</code>
<code> </code><code>def sayhi(self):</code>
<code> </code><code>print </code><code>'hi master , i am your little dog, who do you want me to bite...'</code>
<code> </code><code>favorate_food = </code><code>'bone'</code>
<code> </code><code>self.favorfood = favorate_food #将favorate_food變為全局變量,讓其它方法可以引用</code>
<code> </code><code>def eat(self, food_type):</code>
<code> </code><code>if</code> <code>food_type == self.favorfood: #不同類方法之間的變量調用,需要通過類本身</code>
<code> </code><code>print </code><code>'i like it very much..thanks'</code>
<code> </code><code>else</code><code>:</code>
<code> </code><code>print </code><code>'do not give me this bull shit...'</code>
<code>d = dog()</code>
<code>d.sayhi() </code><code>''</code><code>'如果不調用sayhi(),則無法将favorrate_food變成一個類變量,所謂類變量,</code>
<code> </code><code>即是類下定義的第一級變量</code><code>''</code><code>'</code>
<code>d.eat(</code><code>'bone'</code><code>) </code>
<code>xpleaf@xpleaf-machine:/mnt/hgfs/python/day4$ python class7.py </code>
<code>hi master , i am your little dog, who </code><code>do</code> <code>you want me to bite...</code>
<code>i like it very much..thanks</code>
當然這裡實際上是把sayhi方法中的favorate_food變量變為變中的全局變量,需要注意的是,下面的方法是不可以的:
23
24
25
26
27
<code> </code><code>class_object = </code><code>'food'</code>
<code> </code><code>if</code> <code>food_type == self.sayhi().favorate_food: #這樣使用是不行的</code>
<code>d.sayhi() </code>
<code>traceback (most recent call last):</code>
<code> </code><code>file </code><code>"class7.py"</code><code>, line </code><code>16</code><code>, </code><code>in</code> <code><module></code>
<code> </code><code>d.eat(</code><code>'bone'</code><code>) </code>
<code> </code><code>file </code><code>"class7.py"</code><code>, line </code><code>9</code><code>, </code><code>in</code> <code>eat</code>
<code> </code><code>if</code> <code>food_type == self.sayhi().favorate_food: </code>
<code>attributeerror: </code><code>'nonetype'</code> <code>object has no attribute </code><code>'favorate_food'</code>
上面程式不行是因為,self.sayhi()時,其實會執行sayhi()方法,因為它本來就是一個函數,并且沒有定義傳回值,是以會傳回none值,導緻程式執行出錯。
上面的例子意在說明,不同類方法之間調用變量時,需要将其變為全局變量,并且要通過self即類本身進行引用。即類下的各個方法(函數,name也認為是一個函數),不能獨自互相通信,必須要通過類作為中間人來進行通信,self即代表self本身,通過self.name即可完成通信,是以在定義類下的方法時,必須要加一個參數,即self,來代表類本身,否則這個方法就不能被使用當然參數不一定要是self。
5.類的構造函數__init__()與解構函數__del__()
關于兩個函數的說明,可以看下面一個例子:
28
29
30
31
<code>class</code> <code>person:</code>
<code> </code><code>def __init__(self,name,age): #構造函數,隻要把類執行個體化,就會執行此函數</code>
<code> </code><code>print </code><code>'i am being called right now...'</code>
<code> </code><code>self.name = name</code>
<code> </code><code>self.age = age</code>
<code> </code><code>def sayhi(self): #類的普通方法</code>
<code> </code><code>print </code><code>'hi my name is %s, i am %s years old'</code> <code>% (self.name,self.age)</code>
<code> </code><code>def __del__(self): #解構函數,當執行個體在記憶體中釋放時,才會執行此函數</code>
<code> </code><code>print </code><code>'i got killed just now...bye...'</code><code>, self.name</code>
<code>p = person(</code><code>'alex'</code><code>,</code><code>29</code><code>)</code>
<code>p.sayhi()</code>
<code>#del p</code>
<code>print </code><code>'*'</code><code>*</code><code>60</code>
<code>p2 = person(</code><code>'jack'</code><code>,</code><code>40</code><code>)</code>
<code>p2.sayhi()</code>
<code>#del p2</code>
<code>xpleaf@xpleaf-machine:/mnt/hgfs/python/day4$ python person_class5.py </code>
<code>i am being called right now...</code>
<code>hi my name </code><code>is</code> <code>alex, i am </code><code>29</code> <code>years old</code>
<code>************************************************************</code>
<code>hi my name </code><code>is</code> <code>jack, i am </code><code>40</code> <code>years old</code>
<code>i got killed just now...bye... jack</code>
<code>i got killed just now...bye... alex</code>
可以看到程式最後有兩行相同内容的輸出(除了名字不一樣),是因為程式結束時,會把該程式在記憶體中占用的空間釋放,于是執行兩次解構函數(因為進行了兩次類的執行個體化),才出現這樣的情況為了驗證這個結論,将程式代碼修改為如下并執行:
<code> </code><code>def __init__(self,name,age):</code>
<code> </code><code>def __del__(self):</code>
<code> </code><code>print </code><code>'i got killed just now...bye...'</code><code>, self.name</code>
<code>del p #其它不變,隻是在做類的另一個執行個體前,删除該執行個體</code>
上面的程式代碼及執行結果便可以說明,解構函數在類完成調用并且在記憶體中釋放時才會執行。
6.類的公有屬性與私有屬性及其調用
類的公有屬性即是類下的普通方法,這些方法是可以直接被調用的;而類的私有屬性隻有在類中不同方法之間可以互相調用,一般情況下是不能在類外直接調用的。
可以看下面一個例子:
<code> </code><code>self.__talk() #可以引用類中的私有屬性</code>
<code> </code><code>def __talk(self): #私有屬性的定義方法</code>
<code> </code><code>print </code><code>"i'm private..."</code>
<code>p=person(</code><code>'alex'</code><code>,</code><code>29</code><code>)</code>
<code>xpleaf@xpleaf-machine:/mnt/hgfs/python/day4$ python private6.py </code>
<code>i'm </code><code>private</code><code>...</code>
如果将代碼修改為如下,即在類外調用私有屬性:
<code> </code><code>self.__talk()</code>
<code> </code><code>def __talk(self):</code>
<code>p.__talk() #在類外通過普通方式調用類的私有屬性</code>
<code> </code><code>file </code><code>"private6.py"</code><code>, line </code><code>17</code><code>, </code><code>in</code> <code><module></code>
<code> </code><code>p.__talk()</code>
<code>attributeerror: person instance has no attribute </code><code>'__talk'</code> <code>#會出現找不到類方法的錯誤提示</code>
當然可以通過特殊的方法引用類中的私有屬性:
<code>程式代碼:</code>
<code>print </code><code>'*'</code><code>*</code><code>30</code>
<code>p._person__talk()</code>
<code>******************************</code>
7.類的繼承
類有分父類和子類,在有多個子類的情況下,子類通過繼承父類的屬性或方法,就可以節省很多代碼空間。可以先看下面一個沒有參數傳遞的類的繼承簡單例子:
<code>class</code> <code>person: #父類</code>
<code> </code><code>def tell(self, name):</code>
<code> </code><code>print </code><code>'hi my name is'</code><code>, name</code>
<code>class</code> <code>student(person): #子類,繼承父類</code>
<code> </code><code>def study(sefl):</code>
<code> </code><code>print </code><code>'i am studying py right now.'</code>
<code>s = student()</code>
<code>s.study()</code>
<code>s.tell(</code><code>'mengfanhao'</code><code>) #子類繼承父類後便可以直接調用父類的方法</code>
<code> </code><code>#在有多個需要同時執行相同功能的子類情況下,使用類的繼承可以節省代碼空間</code>
<code>xpleaf@xpleaf-machine:/mnt/hgfs/python/day4$ python cla_with_no_arg9.py </code>
<code>i am studying py right now.</code>
<code>hi my name </code><code>is</code> <code>mengfanhao</code>
看過上面的例子後,再看下面一個含有參數傳遞的例子,通過該例子也可以很好地了解前面第2點中關于類、對象、方法的了解:
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<code>class</code> <code>schoolmember: #父類,學校成員</code>
<code> </code><code>school_name = </code><code>'oldboy linux edu.'</code> <code>#第一級變量,即類屬性</code>
<code> </code><code>def __init__(self,name,gender,nationality = </code><code>'cn'</code><code>): #構造函數</code>
<code> </code><code>self.gender = gender</code>
<code> </code><code>self.nation = nationality</code>
<code> </code><code>def tell(self): #普通的類方法</code>
<code> </code><code>print </code><code>'hi, my name is %s , i am from %s'</code> <code>% (self.name,self.nation)</code>
<code>class</code> <code>student(schoolmember): #子類,學生,繼承父類學校成員的相關屬性</code>
<code> </code><code>def __init__(self, name, gender, class, score, nation = </code><code>'us'</code><code>): #子類下的方法</code>
<code> </code><code>schoolmember.__init__(self, name, gender, nation) #讓父類使用子類傳遞過去的參數</code>
<code> </code><code>self.class = class</code>
<code> </code><code>self.score = score</code>
<code> </code><code>def paytuition(self, amount): #子類下的方法</code>
<code> </code><code>if</code> <code>amount < </code><code>6499</code><code>:</code>
<code> </code><code>print </code><code>'get the fuck off...'</code>
<code> </code><code>print </code><code>'welcome onboard!'</code>
<code>class</code> <code>teacher(schoolmember): #子類,老師,繼承父類學校成員的相關屬性</code>
<code> </code><code>def __init__(self, name, gender, course, salary, nation = </code><code>'fr'</code><code>):</code>
<code> </code><code>schoolmember.__init__(self, name, gender, nation)</code>
<code> </code><code>self.course = course</code>
<code> </code><code>self.salary = salary</code>
<code> </code><code>def teaching(self):</code>
<code> </code><code>print </code><code>'i am teaching %s, i am making %s per month !'</code> <code>% (self.course, self.salary)</code>
<code>s1 = student(</code><code>'wangfanhao'</code><code>, </code><code>'male'</code><code>, </code><code>'python'</code><code>, </code><code>'c+'</code><code>, </code><code>'jp'</code><code>) #執行個體化一個子類對象,學生</code>
<code>s1.tell() #直接繼承父類中的tell()方法</code>
<code>s1.paytuition(</code><code>4999</code><code>) #使用子類student()自身中的類方法</code>
<code>print s1.school_name #直接繼承父類的一個屬性 </code>
<code>s2 = student(</code><code>'shittshirt'</code><code>, </code><code>'male'</code><code>, </code><code>'linux'</code><code>, </code><code>'b'</code><code>)</code>
<code>s2.tell()</code>
<code>s2.paytuition(</code><code>6500</code><code>)</code>
<code>#s2.age = </code><code>29</code>
<code>#print s2.age</code>
<code>t1 = teacher(</code><code>'alex'</code><code>, </code><code>'male'</code><code>, </code><code>'c++'</code><code>, </code><code>5000</code><code>) #執行個體化一個子類對象,學生</code>
<code>t1.tell() #直接繼承父類中的tell()方法</code>
<code>t1.teaching() #直接繼承父類的一個屬性</code>
<code>print </code><code>'s1.name:'</code><code>, s1.name #測試用,觀察輸出結果</code>
<code>print </code><code>'s2.name:'</code><code>, s2.name</code>
<code>print </code><code>'t1.name:'</code><code>, t1.name</code>
<code>xpleaf@xpleaf-machine:/mnt/hgfs/python/day4$ python class_continue8.py</code>
<code>hi, my name </code><code>is</code> <code>wangfanhao , i am from jp</code>
<code>get the fuck off...</code>
<code>oldboy linux edu.</code>
<code>hi, my name </code><code>is</code> <code>shittshirt , i am from us</code>
<code>welcome onboard!</code>
<code>hi, my name </code><code>is</code> <code>alex , i am from fr</code>
<code>i am teaching c++, i am making </code><code>5000</code> <code>per month !</code>
<code>s1.name: wangfanhao</code>
<code>s2.name: shittshirt</code>
<code>t1.name: alex </code>
通過上面的例子便可以很好地了解類的繼承的作用了,即如果需要在多個子類中執行相同的代碼功能時,就可以通過類的繼承來節省代碼空間。
8.類的屬性修改:setattr(),delattr()與getattr()
以上面程式為例,做如下的示範:
首先在互動器中導入該類:
<code>>>> </code><code>import</code> <code>class_continue8</code>
<code>t1.name: alex</code>
<code>>>> </code><code>import</code> <code>tab</code>
<code>>>> class_continue8.</code>
<code>class_continue8.s1 class_continue8.__hash__(</code>
<code>class_continue8.s2 class_continue8.__init__(</code>
<code>class_continue8.schoolmember class_continue8.__name__</code>
<code>class_continue8.student class_continue8.__new__(</code>
<code>class_continue8.t1 class_continue8.__package__</code>
<code>class_continue8.teacher class_continue8.__reduce__(</code>
<code>class_continue8.__class__( class_continue8.__reduce_ex__(</code>
<code>class_continue8.__delattr__( class_continue8.__repr__(</code>
<code>class_continue8.__dict__ class_continue8.__setattr__(</code>
<code>class_continue8.__doc__ class_continue8.__sizeof__(</code>
<code>class_continue8.__file__ class_continue8.__str__(</code>
<code>class_continue8.__format__( class_continue8.__subclasshook__(</code>
<code>class_continue8.__getattribute__(</code>
setattrr():在類中添加屬性
<code>1</code><code>.子類有的屬性,屬于該子類的對象沒有定義,會繼承該屬性;如果有定義,則不會繼承子類的該屬性</code>
<code>>>> class_continue8.student.age #原來student類中并沒有age屬性</code>
<code> </code><code>file </code><code>"<stdin>"</code><code>, line </code><code>1</code><code>, </code><code>in</code> <code><module></code>
<code>attributeerror: </code><code>class</code> <code>student has no attribute </code><code>'age'</code>
<code>>>> setattr(class_continue8.s)</code>
<code>class_continue8.s1 class_continue8.schoolmember</code>
<code>class_continue8.s2 class_continue8.student</code>
<code>>>> setattr(class_continue8.student,</code><code>'age'</code><code>,</code><code>29</code><code>) #在student類中添加age屬性</code>
<code>>>> class_continue8.student.age #再次檢視age屬性</code>
<code>29</code>
<code>>>> class_continue8.s1.age #s1與s2作為student類的執行個體化對象,也會有age屬性</code>
<code>>>> class_continue8.s2.age</code>
<code>>>> setattr(class_continue8.s1,</code><code>'age'</code><code>,</code><code>26</code><code>)</code>
<code>>>> class_continue8.s1.age #此時s1對象有定義,是以不會繼承所屬子類的該屬性</code>
<code>26</code>
<code>2</code><code>.子類的對象有的屬性,但在其所屬子類沒有定義,則該子類不會反繼承該屬性</code>
<code>>>> setattr(class_continue8.s1,</code><code>'tuition'</code><code>,</code><code>5000</code><code>) #如果僅僅是在對象中添加tuition屬性</code>
<code>>>> class_continue8.student.age</code>
<code>>>> class_continue8.student.tuition #則在s1對象所屬的類student中并不會有tuition屬性</code>
<code>attributeerror: </code><code>class</code> <code>student has no attribute </code><code>'tuition'</code>
<code>>>> class_continue8.s1.tuition</code>
<code>5000</code>
<code>>>> class_continue8.s2.tuition #是以s2對象也不會有該屬性</code>
<code>attributeerror: student instance has no attribute </code><code>'tuition'</code>
<code>3</code><code>.父類有的屬性,子類中沒有定義,則子類會繼承該屬性;如果子類中有定義,則不會繼承</code>
<code>>>> setattr(class_continue8.schoolmember,</code><code>'name'</code><code>,</code><code>'python'</code><code>) #在子類在沒有定義</code>
<code>>>> class_continue8.schoolmember.name</code>
<code>'python'</code>
<code>>>> class_continue8.student.name #是以子類會繼承該屬性</code>
<code>>>> setattr(class_continue8.schoolmember,</code><code>'age'</code><code>,</code><code>25</code><code>) #在父類中定義子類有定義的屬性</code>
<code>>>> class_continue8.schoolmember.age</code>
<code>25</code>
<code>>>> class_continue8.student.age #子類已經定義了age屬性,是以不會再繼承父類的該屬性</code>
從上面的例子可以看出,對于父類、子類、對象,屬性的優先級以本地定義的為主,如果本地沒有定義,則繼承上一級的屬性,如果有定義,則使用本地的。
delattr():删除類中的屬性
<code>>>> delattr(class_continue8.schoolmember,</code><code>'name'</code><code>)</code>
<code>attributeerror: </code><code>class</code> <code>schoolmember has no attribute </code><code>'name'</code>
getattr()的主要用法如下:
<code>class</code> <code>student(person):</code>
<code>p = person()</code>
<code>var</code><code>s = [</code><code>'tell'</code><code>, </code><code>'study'</code><code>]</code>
<code>v1 = </code><code>var</code><code>s[</code><code>0</code><code>]</code>
<code>getattr(p, v1)(</code><code>'oldboy'</code><code>)</code>
<code>xpleaf@xpleaf-machine:/mnt/hgfs/python/day4$ python cla_getattr_with_no_arg10.py </code>
<code>hi my name </code><code>is</code> <code>oldboy</code>
對于該程式,如果直接使用p.v1('name')來通路類中的方法是不行的,會有如下的提示錯誤:
<code> </code><code>file </code><code>"cla_getattr_with_no_arg10.py"</code><code>, line </code><code>21</code><code>, </code><code>in</code> <code><module></code>
<code> </code><code>p.v1(</code><code>'oldboy'</code><code>)</code>
<code>attributeerror: person instance has no attribute </code><code>'v1'</code>
是以getattr()的主要用途就在于有一個類有多個方法時,需要對該類的所有方法都執行一遍,那麼可以将類中的方法都寫入一個清單中,再通過getattr()和循環的方法來完成,這樣就可以節省很多代碼空間。