新式類和舊式類
在python2.x的版本中,有“新式類”和“舊式類(也叫做經典類)”之分。新式類是python2.2引進的,在此后的版本中,我們一般用的都是新式類。
定義舊式類:
>>> class AA:
... pass
...
定義新式類的方法:
第一種:
>>> class BB(object):
... pass
...
跟舊式類的區別就在于類的名字后面跟上(object),這其實是一種名為“繼承”的類的操作,當前的類BB是以類object為上級的(object被稱為父類),即BB是繼承自類object的新類。在python3中,所有的類自然地都是類object的子類,就不用彰顯出繼承關系了。
第二種:
在類的前面寫上這么一句:__metaclass__ == type,然后定義類的時候,就不需要在名字后面寫(object)了。
>>> __metaclass__ = type
>>> class CC:
... pass
...
>>> cc = CC()
>>> cc.__class__
<class '__main__.CC'>
>>> type(cc)
<class '__main__.CC'>
創建類
例:定義一個比較常見的類,一般情況下,都是這樣子的。
#!/usr/bin/env python
# coding=utf-8
__metaclass__ = type
class Person:
def __init__(self, name):
self.name = name
def getName(self):
return self.name
def color(self, color):
print "%s is %s" % (self.name, color)
新式類
__metaclass__ = type,意味著下面的類是新式類。
定義類
class Person,這是在聲明創建一個名為"Person"的類。類的名稱一般用大寫字母開頭,這是慣例。如果名稱是兩個單詞,那么兩個單詞的首字母都要大寫,例如class HotPerson,這種命名方法有一個形象的名字,叫做“駝峰式命名”。當然,如果故意不遵循此慣例,也未嘗不可,但是,會給別人閱讀乃至于自己以后閱讀帶來麻煩,不要忘記“代碼通常是給人看的,只是偶爾讓機器執行”。
接下來,分別以縮進表示的,就是這個類的內容了。其實那些東西看起來并不陌生,你一眼就認出它們了——就是已經學習過的函數。沒錯,它們就是函數。不過,很多程序員喜歡把類里面的函數叫做“方法”。是的,就是上節中說到的對象的“方法”。我也看到有人撰文專門分析了“方法”和“函數”的區別。但是,我倒是認為這不重要,重要的是類的中所謂“方法”和前面的函數,在數學角度看,絲毫沒有區別。所以,你盡可以稱之為函數。當然,聽到有人說方法,也不要詫異和糊涂。它們本質是一樣的。
需要再次提醒,函數的命名方法是以def發起,并且函數名稱首字母不要用大寫,可以使用aa_bb的樣式,也可以使用aaBb的樣式,一切看你的習慣了。
不過,要注意的是,類中的函數(方法)的參數跟以往的參數樣式有區別,那就是每個函數必須包括self參數,并且作為默認的第一個參數。這是需要注意的地方。
初始化
def __init__,這個函數是一個比較特殊的,并且有一個名字,叫做初始化函數(注意,很多教材和資料中,把它叫做構造函數,這種說法貌似沒有錯誤,但是一來從字面意義上看,它對應的含義是初始化,二來在python中它的作用和其它語言比如java中的構造函數還不完全一樣,因為還有一個__new__的函數,是真正地構造。所以,在本教程中,我稱之為初始化函數)。它是以兩個下劃線開始,然后是init,最后以兩個下劃線結束。
所謂初始化,就是讓類有一個基本的面貌,而不是空空如也。做很多事情,都要初始化,讓事情有一個具體的起
點狀態。比如你要喝水,必須先初始化杯子里面有水。在python的類中,初始化就擔負著類似的工作。這個工
作是在類被實例化的時候就執行這個函數,從而將初始化的一些屬性可以放到這個函數里面。
此例子中的初始化函數,就意味著實例化的時候,要給參數name提供一個值,作為類初始化的內容。通俗點啰嗦點說,就是在這個類被實例化的同時,要通過name參數傳一個值,這個值被一開始就寫入了類和實例中,成為了類和實例的一個屬性。比如:
girl = Person('sate')
girl是一個實例對象,就如同前面所說的一樣,它有屬性和方法。這里僅說屬性吧。當通過上面的方式實例化后,就自動執行了初始化函數,讓實例girl就具有了name屬性。
很多時候,并不是每次都要從外面傳入數據,有時候會把初始化函數的某些參數設置默認值,如果沒有新的數據傳入,就應用這些默認值。比如:
class Person:
def __init__(self, name, lang="golang", website="www.google.com"):
self.name = name
self.lang = lang
self.website = website
self.email = "qiwsir@gmail.com"
laoqi = Person("LaoQi")
info = Person("qiwsir",lang="python",website="qiwsir.github.io")
print "laoqi.name=",laoqi.name
print "info.name=",info.name
print "-------"
print "laoqi.lang=",laoqi.lang
print "info.lang=",info.lang
print "-------"
print "laoqi.website=",laoqi.website
print "info.website=",info.website
#運行結果
laoqi.name= LaoQi
info.name= qiwsir
-------
laoqi.lang= golang
info.lang= python
-------
laoqi.website= www.google.com
info.website= qiwsir.github.io
函數(方法)
還是回到本節開頭的那個類。構造函數下面的兩個函數:def getName(self),def color(self, color),這兩個函數和前面的初始化函數有共同的地方,即都是以self作為第一個參數。
def getName(self):
return self.name
這個函數中的作用就是返回在初始化時得到的值。
girl = Person('canglaoshi')
name = girl.getName()
girl.getName()就是調用實例girl的方法。調用該方法的時候特別注意,方法名后面的括號不可少,并且括號中不要寫參數,在類中的getName(self)函數第一個參數self是默認的,當類實例化之后,調用此函數的時候,第一個參數不需要賦值。那么,變量name的最終結果就是name = "canglaoshi"。
類和實例
有必要總結一下類和實例的關系:
- “類提供默認行為,是實例的工廠”(源自Learning Python),這句話非常經典,一下道破了類和實例的關系。所謂工廠,就是可以用同一個模子做出很多具體的產品。類就是那個模子,實例就是具體的產品。所以,實例是程序處理的實際對象。
- 類是由一些語句組成,但是實例,是通過調用類生成,每次調用一個類,就得到這個類的新的實例。
- 對于類的:class Person,class是一個可執行的語句。如果執行,就得到了一個類對象,并且將這個類對象賦值給對象名(比如Person)。
self的作用
在Person實例化的過程中girl = Person("canglaoshi"),字符串"canglaoshi"通過初始化函數(__init__())的參數已經存入到內存中,并且以Person類型的面貌存在,組成了一個對象,這個對象和變量girl建立引用關系。這個過程也可說成這些數據附加到一個實例上。這樣就能夠以:object.attribute的形式,在程序中任何地方調用某個數據,例如上面的程序中以girl.name的方式得到"canglaoshi"。這種調用方式,在類和實例中經常使用,點號“.”后面的稱之為類或者實例的屬性。
這是在程序中,并且是在類的外面。如果在類的里面,想在某個地方使用實例化所傳入的數據("canglaoshi"),怎么辦?
在類內部,就是將所有傳入的數據都賦給一個變量,通常這個變量的名字是self。注意,這是習慣,而且是共識,所以,看官不要另外取別的名字了。
在初始化函數中的第一個參數self,就是起到了這個作用——接收實例化過程中傳入的所有數據,這些數據是初始化函數后面的參數導入的。顯然,self應該就是一個實例(準確說法是應用實例),因為它所對應的就是具體數據。
如果將上面的類稍加修改,看看效果:
#!/usr/bin/env python
# coding=utf-8
__metaclass__ = type
class Person:
def __init__(self, name):
self.name = name
print self #新增
print type(self) #新增
其它部分省略。當初始化的時候,就首先要運行構造函數,同時就打印新增的兩條。結果是
<__main__.Person object at 0xb7282cec>
<class '__main__.Person'>
證實了推理。self就是一個實例(準確說是實例的引用變量)。
self這個實例跟前面說的那個girl所引用的實例對象一樣,也有屬性。那么,接下來就規定其屬性和屬性對應的數據。上面代碼中:
self.name = name
就是規定了self實例的一個屬性,這個屬性的名字也叫做name,這個屬性的值等于初始化函數的參數name所導入的數據。注意,self.name中的name和初始化函數的參數name沒有任何關系,它們兩個一樣,只不過是一種起巧合(經常巧合,其實是為了省事和以后識別方便,故意讓它們巧合。),或者說是寫代碼的人懶惰,不想另外取名字而已,無他。當然,如果寫成self.xxxooo = name,也是可以的。
其實,從效果的角度來理解,這么理解更簡化:類的實例girl對應著self,girl通過self導入實例屬性的所有數據。
當然,self的屬性數據,也不一定非得是由參數傳入的,也可以在構造函數中自己設定。比如:
#!/usr/bin/env python
#coding:utf-8
__metaclass__ = type
class Person:
def __init__(self, name):
self.name = name
self.email = "qiwsir@gmail.com" #這個屬性不是通過參數傳入的
info = Person("qiwsir") #換個字符串和實例化變量
print "info.name=",info.name
print "info.email=",info.email #info通過self建立實例,并導入實例屬性數據
運行結果
info.name= qiwsir
info.email= qiwsir@gmail.com #打印結果
通過這個例子,其實讓我們拓展了對self的認識,也就是它不僅僅是為了在類內部傳遞參數導入的數據,還能在初始化函數中,通過self.attribute的方式,規定self實例對象的屬性,這個屬性也是類實例化對象的屬性,即做為類通過初始化函數初始化后所具有的屬性。所以在實例info中,通過info.email同樣能夠得到該屬性的數據。在這里,就可以把self形象地理解為“內外兼修”了。或者按照前面所提到的,將info和self對應起來,self主內,info主外。