python類的使用

hiekay發表於2018-12-26

建立例項

說明:關於類的這部分,我參考了《Learning Python》一書的講解。

建立類

建立類的方法比較簡單,如下:

class Person:
注意,類的名稱一般用大寫字母開頭,這是慣例。

接下來,一般都要編寫建構函式,在寫這個函式之前,先解釋一下什麼是建構函式。

class Person:

def __init__(self, name, lang, website):
    self.name = name
    self.lang = lang
    self.website = website

上面的類中,首先呈現出來的是一個名為:__init__()的函式,注意,這個函式是以兩個下劃線開始,然後是init,最後以兩個下劃線結束。這是一個函式,就跟我們此前學習過的函式一樣的函式。但是,這個函式又有點奇特,它的命名是用“__”開始和結束。

在這裡要明確一個基本概念,類就是一種物件型別,和跟前面學習過的數值、字串、列表等等型別一樣。比如這裡構建的類名字叫做Person,那麼就是我們要試圖建立一種物件型別,這種型別被稱之為Person,就如同有一種物件型別是list一樣。

在構建Person類的時候,首先要做的就是對這種型別進行初始化,也就是要說明這種型別的基本結構,一旦這個型別的物件被呼叫了,第一件事情就是要執行這個型別的基本結構,也就是類Person的基本結構。就好比我們每個人,在頭腦中都有關於“人”這樣一個物件型別(對應著類),一旦遇到張三(張三是一個具體人),我們首先執行“人”這個類的基本結構:一個鼻子兩隻眼,鼻子下面一張嘴。如果張三符合這個基本機構,我們不會感到驚詫(不報錯),如果張三不符合這個基本結構(比如三隻眼睛),我們就會感到驚詫(報錯了)。

由於類是我們自己構造的,那麼基本結構也是我們自己手動構造的。在類中,基本結構是寫在__init__()這個函式裡面。故這個函式稱為建構函式,擔負著對類進行初始化的任務。

還是回到Person這個類,如果按照上面的程式碼,寫好了,是不是__init__()就執行起來了呢?不是!這時候還沒有看到張三呢,必須看到張三才能執行。所謂看到張三,看到張三這樣一個具體的實實在在的人,此動作,在python中有一個術語,叫做例項化。當類Person例項化後立刻執行__init__()函式。

#!/usr/bin/env python
#coding:utf-8

class Person:
    def __init__(self, name, lang, website):
        self.name = name
        self.lang = lang
        self.website = website

info = Person("hiekay","python","hiekay.github.io")     #例項化Person
print "info.name=",info.name
print "info.lang=",info.lang
print "info.website=",info.website

#上面程式碼的執行結果:

info.name= hiekay
info.lang= python
info.website= hiekay.github.io

在上面的程式碼中,建立的類Person,建構函式申明瞭這個類的基本結構:name,lang,website。

注意觀察:info=Person(“hiekay”,”python”,”hiekay.github.io”),這句話就是將類Person例項化了。也就是在記憶體中建立了一個物件,這個物件的型別是Person型別,這個Person型別是什麼樣子的呢?就是__init__()所構造的那樣。在例項化時,必須通過引數傳入具體的資料:name=”hiekay”,lang=”python”,website=”hiekay.github.io”。這樣在記憶體中就存在了一個物件,這個物件的型別是Person,然後通過賦值語句,與變數info建立引用關係。請看官回憶以前已經講述過的變數和物件的引用關係。

類、例項,這兩個概念會一直伴隨著後續的學習,並且,在很多的OOP模型中,都會遇到這兩個概念。為了讓看官不暈乎,這裡將它們進行比較(注意:比較的內容,參考了《Learning Python》一書)

類和例項

“類提供預設行為,是例項的工廠”,我覺得這句原話非常經典,一下道破了類和例項的關係。看上面程式碼,體會一下,是不是這個理?所謂工廠,就是可以用同一個模子做出很多具體的產品。類就是那個模子,例項就是具體的產品。所以,例項是程式處理的實際物件。
類是由一些語句組成,但是例項,是通過呼叫類生成,每次呼叫一個類,就得到這個類的新的例項。
對於類的:class Person,class是一個可執行的語句。如果執行,就得到了一個類物件,並且將這個類物件賦值給物件名(比如Person)。

self的作用

在建構函式中,第一個引數是self,但是在例項化的時候,似乎沒有這個引數什麼事兒,那麼self是幹什麼的呢?

self是一個很神奇的引數。

在Person例項化的過程中,資料”hiekay”,”python”,”hiekay.github.io”通過建構函式(__init__())的引數已經存入到記憶體中,並且這些資料以Person型別的面貌存在組成一個物件,這個物件和變數info建立的引用關係。這個過程也可說成這些資料附加到一個例項上。這樣就能夠以:object.attribute的形式,在程式中任何地方呼叫某個資料,例如上面的程式中以info.name得到”hiekay”這個資料。這種呼叫方式,在類和例項中經常使用,點號“.”後面的稱之為類或者例項的屬性。

這是在程式中,並且是在類的外面。如果在類的裡面,想在某個地方使用傳入的資料,怎麼辦?

在類內部,我們會寫很多不同功能的函式,這些函式在類裡面有另外一個名稱,曰:方法。那麼,通過類的建構函式中的引數傳入的這些資料也想在各個方法中被使用,就需要在類中長久儲存並能隨時呼叫這些資料。為了解決這個問題,在類中,所有傳入的資料都賦給一個變數,通常這個變數的名字是self。注意,這是習慣,而且是共識,所以,看官不要另外取別的名字了。

在建構函式中的第一個引數self,就是起到了這個作用——接收例項化過程中傳入的所有資料,這些資料是通過建構函式後面的引數匯入的。顯然,self應該就是一個例項(準確說法是應用例項),因為它所對應的就是具體資料。

如果將上面的類增加兩句,看看效果:

#!/usr/bin/env python
#coding:utf-8

class Person:
    def __init__(self, name, lang, website):
        self.name = name
        self.lang = lang
        self.website = website

        print self          #列印,看看什麼結果
        print type(self)

執行結果

<__main__.Person instance at 0xb74a45cc>
<type `instance`>

證實了推理。self就是一個例項(準確說是例項的引用變數)。

self這個例項跟前面說的那個info所引用的例項物件一樣,也有屬性。那麼,接下來就規定其屬性和屬性對應的資料。上面程式碼中:self.name = name,就是規定了self例項的一個屬性,這個屬性的名字也叫做name,這個屬性的資料等於建構函式的引數name所匯入的資料。注意,self.name中的name和建構函式的引數name沒有任何關係,它們兩個一樣,只不過是一種起巧合(經常巧合),或者說是寫程式碼的人懶惰,不想另外取名字而已,無他。當然,如果寫成self.xxxooo = name,也是可以的。

其實,從效果的角度來理解,可能更簡單一些,那就是類的例項info對應著self,info通過self匯入例項屬性的所有資料。

當然,self的屬性資料,也不一定非得是由引數傳入的,也可以在建構函式中自己設定。比如:

#!/usr/bin/env python
#coding:utf-8

class Person:
    def __init__(self, name, lang, website):
        self.name = name
        self.lang = lang
        self.website = website
        self.email = "hiekay@gmail.com"     #這個屬性不是通過引數傳入的

info = Person("hiekay","python","hiekay.github.io")
print "info.name=",info.name
print "info.lang=",info.lang
print "info.website=",info.website
print "info.email=",info.email      #info通過self建立例項,並匯入例項屬性資料

執行結果

info.name= hiekay
info.lang= python
info.website= hiekay.github.io
info.email= hiekay@gmail.com    #列印結果

通過這個例子,其實讓我們擴充了對self的認識,也就是它不僅僅是為了在類內部傳遞引數匯入的資料,還能在建構函式中,通過self.attribute的方式,規定self例項物件的屬性,這個屬性也是類例項化物件的屬性,即做為類通過建構函式初始化後所具有的屬性。所以在例項info中,通過info.email同樣能夠得到該屬性的資料。在這裡,就可以把self形象地理解為“內外兼修”了。或者按照前面所提到的,將info和self對應起來,self主內,info主外。

其實,self的話題還沒有結束,後面的方法中還會出現它。它真的神奇呀。

建構函式的引數

前面已經說過了,建構函式__init__就是一個函式,只不過長相有點古怪罷了。那麼,函式中的操作在建構函式中依然可行。比如:

def __init__(self,*args):

pass

這種型別的引數:*args和前面講述函式引數一樣。但是,self這個引數是必須的,因為它要來建立例項物件。

很多時候,並不是每次都要從外面傳入資料,有時候會把建構函式的某些引數設定預設值,如果沒有新的資料傳入,就應用這些預設值。比如:

class Person:
    def __init__(self, name, lang="golang", website="www.google.com"):
        self.name = name
        self.lang = lang
        self.website = website
        self.email = "hiekay@gmail.com"

hiekay = Person("hiekay")     #匯入一個資料name="hiekay",其它預設值
info = Person("hiekay",lang="python",website="hiekay.github.io")    #全部重新匯入資料

print "hiekay.name=",hiekay.name
print "info.name=",info.name
print "-------"
print "hiekay.lang=",hiekay.lang
print "info.lang=",info.lang
print "-------"
print "hiekay.website=",hiekay.website
print "info.website=",info.website

執行結果

hiekay.name= hiekay
info.name= hiekay
-------
hiekay.lang= golang
info.lang= python
-------
hiekay.website= www.google.com
info.website= hiekay.github.io

在這段程式碼中,看官首先要體會一下,“類是例項的工廠”這句話的含義,通過類Person生成了兩個例項:hiekay、info

此外,在看函式賦值的情況,允許設定預設引數值。

至此,僅僅是初步構建了一個類的基本結構,完成了類的初始化。


相關文章