天天看點

【Python之旅】第四篇(三):Python面向對象程式設計詳解

終于是來到了python的面向對象程式設計,以前是沒有接觸過其它的面向對象程式設計的語言,是以學習這一部分是相當帶勁的,這裡也總結一下。

1.面向對象程式設計的相關名詞及解釋

    世界萬物,皆可分類,一切皆為對象。

    所謂的面向對象程式設計,指的是一種程式設計的思想,通過對具體代碼實作過程(面向過程程式設計)的不斷抽象,以形成一個個的類别,以提高我們進行大型程式編寫的效率(面向對象的具體實作需要面向過程,大型程式也可以用面向過程來編寫,隻是比較麻煩)。對于面向對象程式設計的相關名詞和解釋如下:

對象 :類的實體\一個叫rain的好色的男人

類:人\動物\機器

方法:人會走,會思考\狗會叫,會咬人\定義一個類的各個功能

消息傳遞:狗叫了,人聽見了,就叫通信

繼承:狗都四條腿走路

封裝:人不能引用狗的特性,比如四條腿走路

多态性:一個叫的功能,可能是低吼,也可是大聲叫

抽象性:簡化複雜的現實問題的途徑,它可以為具體問題找到最恰當的類定義

    對“類”這個名詞舉個簡單的例子:

杯子:類

這個杯子:對象(實體),屬于“杯子”這個類

    對“方法”這個名詞舉個例子:動态方法(動态屬性)與靜态方法(靜态方法可以了解為類的屬性)。

2.類、對象、方法

    關于三者的關系,用下面的一張圖檔可以非常清晰明确的說明:

【Python之旅】第四篇(三):Python面向對象程式設計詳解

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>&lt;module&gt;</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>&lt;module&gt;</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 &lt; </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>&gt;&gt;&gt; </code><code>import</code> <code>class_continue8</code>

<code>t1.name: alex</code>

<code>&gt;&gt;&gt; </code><code>import</code> <code>tab</code>

<code>&gt;&gt;&gt; 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>&gt;&gt;&gt; class_continue8.student.age    #原來student類中并沒有age屬性</code>

<code>  </code><code>file </code><code>"&lt;stdin&gt;"</code><code>, line </code><code>1</code><code>, </code><code>in</code> <code>&lt;module&gt;</code>

<code>attributeerror: </code><code>class</code> <code>student has no attribute </code><code>'age'</code>

<code>&gt;&gt;&gt; setattr(class_continue8.s)</code>

<code>class_continue8.s1            class_continue8.schoolmember</code>

<code>class_continue8.s2            class_continue8.student</code>

<code>&gt;&gt;&gt; setattr(class_continue8.student,</code><code>'age'</code><code>,</code><code>29</code><code>)    #在student類中添加age屬性</code>

<code>&gt;&gt;&gt; class_continue8.student.age    #再次檢視age屬性</code>

<code>29</code>

<code>&gt;&gt;&gt; class_continue8.s1.age    #s1與s2作為student類的執行個體化對象,也會有age屬性</code>

<code>&gt;&gt;&gt; class_continue8.s2.age</code>

<code>&gt;&gt;&gt; setattr(class_continue8.s1,</code><code>'age'</code><code>,</code><code>26</code><code>)</code>

<code>&gt;&gt;&gt; class_continue8.s1.age        #此時s1對象有定義,是以不會繼承所屬子類的該屬性</code>

<code>26</code>

<code>2</code><code>.子類的對象有的屬性,但在其所屬子類沒有定義,則該子類不會反繼承該屬性</code>

<code>&gt;&gt;&gt; setattr(class_continue8.s1,</code><code>'tuition'</code><code>,</code><code>5000</code><code>)    #如果僅僅是在對象中添加tuition屬性</code>

<code>&gt;&gt;&gt; class_continue8.student.age</code>

<code>&gt;&gt;&gt; class_continue8.student.tuition    #則在s1對象所屬的類student中并不會有tuition屬性</code>

<code>attributeerror: </code><code>class</code> <code>student has no attribute </code><code>'tuition'</code>

<code>&gt;&gt;&gt; class_continue8.s1.tuition</code>

<code>5000</code>

<code>&gt;&gt;&gt; class_continue8.s2.tuition        #是以s2對象也不會有該屬性</code>

<code>attributeerror: student instance has no attribute </code><code>'tuition'</code>

<code>3</code><code>.父類有的屬性,子類中沒有定義,則子類會繼承該屬性;如果子類中有定義,則不會繼承</code>

<code>&gt;&gt;&gt; setattr(class_continue8.schoolmember,</code><code>'name'</code><code>,</code><code>'python'</code><code>)    #在子類在沒有定義</code>

<code>&gt;&gt;&gt; class_continue8.schoolmember.name</code>

<code>'python'</code>

<code>&gt;&gt;&gt; class_continue8.student.name    #是以子類會繼承該屬性</code>

<code>&gt;&gt;&gt; setattr(class_continue8.schoolmember,</code><code>'age'</code><code>,</code><code>25</code><code>)    #在父類中定義子類有定義的屬性</code>

<code>&gt;&gt;&gt; class_continue8.schoolmember.age</code>

<code>25</code>

<code>&gt;&gt;&gt; class_continue8.student.age    #子類已經定義了age屬性,是以不會再繼承父類的該屬性</code>

    從上面的例子可以看出,對于父類、子類、對象,屬性的優先級以本地定義的為主,如果本地沒有定義,則繼承上一級的屬性,如果有定義,則使用本地的。

delattr():删除類中的屬性

<code>&gt;&gt;&gt; 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>&lt;module&gt;</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()和循環的方法來完成,這樣就可以節省很多代碼空間。