在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 類(2)
標(biāo)準(zhǔn)庫 (4)
如何成為 Python 高手
標(biāo)準(zhǔn)庫 (6)
標(biāo)準(zhǔn)庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習(xí)
列表(3)
從小工到專家
除法
錯(cuò)誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準(zhǔn)備
函數(shù)練習(xí)
標(biāo)準(zhǔn)庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個(gè)簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯(cuò)誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標(biāo)準(zhǔn)庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實(shí)戰(zhàn)
運(yùn)算符
類(3)
字典(2)
語句(1)
數(shù)和四則運(yùn)算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標(biāo)準(zhǔn)庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關(guān)于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學(xué)函數(shù)和運(yùn)算優(yōu)先級
字符串(3)
為計(jì)算做準(zhǔn)備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯(cuò)誤和異常 (3)
分析 Hello
Python 安裝
標(biāo)準(zhǔn)庫 (2)
列表(2)
元組

類(2)

現(xiàn)在開始不用偽代碼了,用真正的 Python 代碼來理解類。當(dāng)然,例子還是要用讀者感興趣的例子。

新式類和舊式類

因?yàn)?Python 是一個(gè)不斷發(fā)展的高級語言(似乎別的語言是不斷發(fā)展的,甚至于自然語言也是),導(dǎo)致了在 Python2.x 的版本中,有“新式類”和“舊式類(也叫做經(jīng)典類)”之分。新式類是 Python2.2 引進(jìn)的,在此后的版本中,我們一般用的都是新式類。本著知其然還要知其所以然的目的,簡單回顧一下兩者的差別。

>>> class AA:
...     pass
... 

這是定義了一個(gè)非常簡單的類,而且是舊式類。至于如何定義類,下面會詳細(xì)說明。讀者姑且囫圇吞棗似的的認(rèn)同我剛才建立的名為 AA 的類,為了簡單,這個(gè)類內(nèi)部什么也不做,就是用 pass 一帶而過。但不管怎樣,是一個(gè)類,而且是一個(gè)舊式類(或曰經(jīng)典類)

然后,將這個(gè)類實(shí)例化(還記得上節(jié)中實(shí)例化嗎?對,就是那個(gè)王美女干的事情):

>>> aa = AA()

不要忘記,實(shí)例化的時(shí)候,類的名稱后面有一對括號。接下來做如下操作:

>>> type(AA)
<type 'classobj'>
>>> aa.__class__
<class __main__.AA at 0xb71f017c>
>>> type(aa)
<type 'instance'>

解讀一下上面含義:

  • type(AA):查看類 AA 的類型,返回的是'classobj'
  • aa.__class__:aa 是一個(gè)實(shí)例,也是一個(gè)對象,每個(gè)對象都有__class__屬性,用于顯示它的類型。這里返回的結(jié)果是<class __main__.AA at 0xb71f017c>,從這個(gè)結(jié)果中可以讀出的信息是,aa 是類 AA 的實(shí)例,并且類 AA 在內(nèi)存中的地址是 0xb71f017c。
  • type(aa):是要看實(shí)例 aa 的類型,它顯示的結(jié)果是'instance,意思是告訴我們它的類型是一個(gè)實(shí)例。

在這里是不是有點(diǎn)感覺不和諧呢?aa.__class__type(aa) 都可以查看對象類型,但是它們居然顯示不一樣的結(jié)果。比如,查看這個(gè)對象:

>>> a = 7
>>> a.__class__
<type 'int'>
>>> type(a)
<type 'int'>

別忘記了,前面提到過的“萬物皆對象”,那么一個(gè)整數(shù)7也是對象,用兩種方式查看,返回的結(jié)果一樣。為什么到類(嚴(yán)格講是舊式類)這里,居然返回不一樣呢?太不和諧了。

于是乎,就有了新式類,從 Python2.2 開始,變成這樣了:

>>> class BB(object):
...     pass
... 

>>> bb = BB()

>>> bb.__class__
<class '__main__.BB'>
>>> type(bb)
<class '__main__.BB'>

終于把兩者統(tǒng)一起來了,世界和諧了。

這就是新式類和舊式類的不同。

當(dāng)然,不同點(diǎn)絕非僅僅于此,這里只不過提到一個(gè)現(xiàn)在能夠理解的不同罷了。另外的不同還在于兩者對于多重繼承的查找和調(diào)用方法不同,舊式類是深度優(yōu)先,新式類是廣度優(yōu)先??梢韵炔焕斫猓竺鏁龅降?。

不管是新式類、還是舊式類,都可以通過這樣的方法查看它們在內(nèi)存中的存儲空間信息

>>> print aa
<__main__.AA instance at 0xb71efd4c>

>>> print bb
<__main__.BB object at 0xb71efe6c>

分別告訴了我們兩個(gè)實(shí)例是基于誰生成的,不過還是稍有區(qū)別。

知道了舊式類和新式類,那么下面的所有內(nèi)容,就都是對新式類而言。“喜新厭舊”不是編程經(jīng)常干的事情嗎?所以,舊式類就不是我們討論的內(nèi)容了。

還要注意,如果你用的是 Python3,就不用為新式類和舊式類而擔(dān)心了,因?yàn)樵?Python3 中壓根兒就沒有這個(gè)問題存在。

如何定義新式類呢?

第一種定義方法,就是如同前面那樣:

>>> class BB(object):
...     pass
...

跟舊式類的區(qū)別就在于類的名字后面跟上 (object),這其實(shí)是一種名為“繼承”的類的操作,當(dāng)前的類 BB 是以類 object 為上級的(object 被稱為父類),即 BB 是繼承自類 object 的新類。在 Python3 中,所有的類自然地都是類 object 的子類,就不用彰顯出繼承關(guān)系了。對了,這里說的有點(diǎn)讓讀者糊涂,因?yàn)槊俺鰜砹恕袄^承”、“父類”、“子類”,不用著急,繼續(xù)向下看。下面精彩,并且能解惑。

第二種定義方法,在類的前面寫上這么一句:__metaclass__ == type,然后定義類的時(shí)候,就不需要在名字后面寫(object)了。

>>> __metaclass__ = type
>>> class CC:
...     pass
... 
>>> cc = CC()
>>> cc.__class__
<class '__main__.CC'>
>>> type(cc)
<class '__main__.CC'>

兩種方法,任你選用,沒有優(yōu)劣之分。

創(chuàng)建類

因?yàn)樵谝话闱闆r下,一個(gè)類都不是兩三行能搞定的。所以,下面可能很少使用交互模式了,因?yàn)槟菢右坏┯幸稽c(diǎn)錯(cuò)誤,就前功盡棄。我改用編輯界面。你用什么工具編輯?Python 自帶一個(gè) IDE,可以使用。我習(xí)慣用 vim。你用你習(xí)慣的工具即可。如果你沒有別的工具,就用安裝 Python 是自帶的那個(gè) IDE。

#!/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)

上面定義的是一個(gè)比較常見的類,一般情況下,都是這樣子的。下面對這個(gè)“大眾臉”的類一一解釋。

新式類

__metaclass__ = type,意味著下面的類是新式類。

定義類

class Person,這是在聲明創(chuàng)建一個(gè)名為"Person"的類。類的名稱一般用大寫字母開頭,這是慣例。如果名稱是兩個(gè)單詞,那么兩個(gè)單詞的首字母都要大寫,例如 class HotPerson,這種命名方法有一個(gè)形象的名字,叫做“駝峰式命名”。當(dāng)然,如果故意不遵循此慣例,也未嘗不可,但是,會給別人閱讀乃至于自己以后閱讀帶來麻煩,不要忘記“代碼通常是給人看的,只是偶爾讓機(jī)器執(zhí)行”。既然大家都是靠右走的,你就別非要在路中間睡覺了。

接下來,分別以縮進(jìn)表示的,就是這個(gè)類的內(nèi)容了。其實(shí)那些東西看起來并不陌生,你一眼就認(rèn)出它們了——就是已經(jīng)學(xué)習(xí)過的函數(shù)。沒錯(cuò),它們就是函數(shù)。不過,很多程序員喜歡把類里面的函數(shù)叫做“方法”。是的,就是上節(jié)中說到的對象的“方法”。我也看到有人撰文專門分析了“方法”和“函數(shù)”的區(qū)別。但是,我倒是認(rèn)為這不重要,重要的是類的中所謂“方法”和前面的函數(shù),在數(shù)學(xué)角度看,絲毫沒有區(qū)別。所以,你盡可以稱之為函數(shù)。當(dāng)然,聽到有人說方法,也不要詫異和糊涂。它們本質(zhì)是一樣的。

需要再次提醒,函數(shù)的命名方法是以 def 發(fā)起,并且函數(shù)名稱首字母不要用大寫,可以使用 aa_bb 的樣式,也可以使用 aaBb 的樣式,一切看你的習(xí)慣了。

不過,要注意的是,類中的函數(shù)(方法)的參數(shù)跟以往的參數(shù)樣式有區(qū)別,那就是每個(gè)函數(shù)必須包括 self 參數(shù),并且作為默認(rèn)的第一個(gè)參數(shù)。這是需要注意的地方。至于它的用途,繼續(xù)學(xué)習(xí)即可知道。

初始化

def __init__,這個(gè)函數(shù)是一個(gè)比較特殊的,并且有一個(gè)名字,叫做初始化函數(shù)(注意,很多教材和資料中,把它叫做構(gòu)造函數(shù),這種說法貌似沒有錯(cuò)誤,但是一來從字面意義上看,它對應(yīng)的含義是初始化,二來在 Python 中它的作用和其它語言比如 java 中的構(gòu)造函數(shù)還不完全一樣,因?yàn)檫€有一個(gè)__new__的函數(shù),是真正地構(gòu)造。所以,在本教程中,我稱之為初始化函數(shù))。它是以兩個(gè)下劃線開始,然后是 init,最后以兩個(gè)下劃線結(jié)束。

所謂初始化,就是讓類有一個(gè)基本的面貌,而不是空空如也。做很多事情,都要初始化,讓事情有一個(gè)具體的起點(diǎn)狀態(tài)。比如你要喝水,必須先初始化杯子里面有水。在 Python 的類中,初始化就擔(dān)負(fù)著類似的工作。這個(gè)工作是在類被實(shí)例化的時(shí)候就執(zhí)行這個(gè)函數(shù),從而將初始化的一些屬性可以放到這個(gè)函數(shù)里面。

此例子中的初始化函數(shù),就意味著實(shí)例化的時(shí)候,要給參數(shù) name 提供一個(gè)值,作為類初始化的內(nèi)容。通俗點(diǎn)啰嗦點(diǎn)說,就是在這個(gè)類被實(shí)例化的同時(shí),要通過 name 參數(shù)傳一個(gè)值,這個(gè)值被一開始就寫入了類和實(shí)例中,成為了類和實(shí)例的一個(gè)屬性。比如:

girl = Person('wangguniang')

girl 是一個(gè)實(shí)例對象,就如同前面所說的一樣,它有屬性和方法。這里僅說屬性吧。當(dāng)通過上面的方式實(shí)例化后,就自動執(zhí)行了初始化函數(shù),讓實(shí)例 girl 就具有了 name 屬性。

print girl.name

執(zhí)行這句話的結(jié)果是打印出 wangguniang

這就是初始化的功能。簡而言之,通過初始化函數(shù),確定了這個(gè)實(shí)例(類)的“基本屬性”(實(shí)例是什么樣子的)。比如上面的實(shí)例化之后,就確立了實(shí)例 girl 的 name 是"wangguniang"。

初始化函數(shù),就是一個(gè)函數(shù),所以,它的參數(shù)設(shè)置,也符合前面學(xué)過的函數(shù)參數(shù)設(shè)置規(guī)范。比如

def __init__(self,*args):
    pass

這種類型的參數(shù):*args 和前面講述函數(shù)參數(shù)一樣,就不多說了。忘了的看官,請去復(fù)習(xí)。但是,self 這個(gè)參數(shù)是必須的。

很多時(shí)候,并不是每次都要從外面?zhèn)魅霐?shù)據(jù),有時(shí)候會把初始化函數(shù)的某些參數(shù)設(shè)置默認(rèn)值,如果沒有新的數(shù)據(jù)傳入,就應(yīng)用這些默認(rèn)值。比如:

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

#運(yùn)行結(jié)果

laoqi.name= LaoQi
info.name= qiwsir
-------
laoqi.lang= golang
info.lang= python
-------
laoqi.website= www.google.com
info.website= qiwsir.github.io

在編程界,有這樣一句話,說“類是實(shí)例工廠”,什么意思呢?工廠是干什么的?生產(chǎn)物品,比如生產(chǎn)電腦。一個(gè)工廠可以生產(chǎn)好多電腦。那么,類,就能“生產(chǎn)”好多實(shí)例,所以,它是“工廠”。比如上面例子中,就有兩個(gè)實(shí)例。

函數(shù)(方法)

還是回到本節(jié)開頭的那個(gè)類。構(gòu)造函數(shù)下面的兩個(gè)函數(shù):def getName(self),def color(self, color),這兩個(gè)函數(shù)和前面的初始化函數(shù)有共同的地方,即都是以 self 作為第一個(gè)參數(shù)。

def getName(self):
    return self.name

這個(gè)函數(shù)中的作用就是返回在初始化時(shí)得到的值。

girl = Person('wangguniang')
name = girl.getName()

girl.getName()就是調(diào)用實(shí)例 girl 的方法。調(diào)用該方法的時(shí)候特別注意,方法名后面的括號不可少,并且括號中不要寫參數(shù),在類中的 getName(self) 函數(shù)第一個(gè)參數(shù) self 是默認(rèn)的,當(dāng)類實(shí)例化之后,調(diào)用此函數(shù)的時(shí)候,第一個(gè)參數(shù)不需要賦值。那么,變量 name 的最終結(jié)果就是 name = "wangguniang"。

同樣道理,對于方法:

def color(self, color):
    print "%s is %s" % (self.name, color)

也是在實(shí)例化之后調(diào)用:

girl.color("white")

這也是在執(zhí)行實(shí)例化方法,只是由于類中的該方法有兩個(gè)參數(shù),除了默認(rèn)的 self 之外,還有一個(gè) color,所以,在調(diào)用這個(gè)方法的時(shí)候,要為后面那個(gè)參數(shù)傳值了。

至此,已經(jīng)將這個(gè)典型的類和調(diào)用方法分解完畢,把全部代碼完整貼出,請讀者在從頭到尾看看,是否理解了每個(gè)部分的含義:

#!/usr/bin/env Python
# coding=utf-8

__metaclass__ = type             #新式類

class Person:                    #創(chuàng)建類
    def __init__(self, name):    #構(gòu)造函數(shù)
        self.name = name

    def getName(self):           #類中的方法(函數(shù))
        return self.name

    def color(self, color):
        print "%s is %s" % (self.name, color)

girl = Person('wangguniang')      #實(shí)例化
name = girl.getName()            #調(diào)用方法(函數(shù)) 
print "the person's name is: ", name
girl.color("white")              #調(diào)用方法(函數(shù))

print "------"
print girl.name                  #實(shí)例的屬性

保存后,運(yùn)行得到如下結(jié)果:

$ python 20701.py 
the person's name is:  wangguniang
wangguniang is white
------
wangguniang

類和實(shí)例

有必要總結(jié)一下類和實(shí)例的關(guān)系:

  • “類提供默認(rèn)行為,是實(shí)例的工廠”(源自 Learning Python),這句話非常經(jīng)典,一下道破了類和實(shí)例的關(guān)系。所謂工廠,就是可以用同一個(gè)模子做出很多具體的產(chǎn)品。類就是那個(gè)模子,實(shí)例就是具體的產(chǎn)品。所以,實(shí)例是程序處理的實(shí)際對象。
  • 類是由一些語句組成,但是實(shí)例,是通過調(diào)用類生成,每次調(diào)用一個(gè)類,就得到這個(gè)類的新的實(shí)例。
  • 對于類的:class Person,class 是一個(gè)可執(zhí)行的語句。如果執(zhí)行,就得到了一個(gè)類對象,并且將這個(gè)類對象賦值給對象名(比如 Person)。

也許上述比較還不足以讓看官理解類和實(shí)例,沒關(guān)系,繼續(xù)學(xué)習(xí),在前進(jìn)中排除疑惑。

self 的作用

類里面的函數(shù),第一個(gè)參數(shù)是 self,但是在實(shí)例化的時(shí)候,似乎沒有這個(gè)參數(shù)什么事兒,那么 self 是干什么的呢?

self 是一個(gè)很神奇的參數(shù)。

在 Person 實(shí)例化的過程中 girl = Person("wangguniang"),字符串"wangguniang"通過初始化函數(shù)(__init__())的參數(shù)已經(jīng)存入到內(nèi)存中,并且以 Person 類型的面貌存在,組成了一個(gè)對象,這個(gè)對象和變量 girl 建立引用關(guān)系。這個(gè)過程也可說成這些數(shù)據(jù)附加到一個(gè)實(shí)例上。這樣就能夠以:object.attribute的形式,在程序中任何地方調(diào)用某個(gè)數(shù)據(jù),例如上面的程序中以 girl.name 的方式得到"wangguniang"。這種調(diào)用方式,在類和實(shí)例中經(jīng)常使用,點(diǎn)號“.”后面的稱之為類或者實(shí)例的屬性。

這是在程序中,并且是在類的外面。如果在類的里面,想在某個(gè)地方使用實(shí)例化所傳入的數(shù)據(jù)("wangguniang"),怎么辦?

在類內(nèi)部,就是將所有傳入的數(shù)據(jù)都賦給一個(gè)變量,通常這個(gè)變量的名字是 self。注意,這是習(xí)慣,而且是共識,所以,看官不要另外取別的名字了。

在初始化函數(shù)中的第一個(gè)參數(shù) self,就是起到了這個(gè)作用——接收實(shí)例化過程中傳入的所有數(shù)據(jù),這些數(shù)據(jù)是初始化函數(shù)后面的參數(shù)導(dǎo)入的。顯然,self 應(yīng)該就是一個(gè)實(shí)例(準(zhǔn)確說法是應(yīng)用實(shí)例),因?yàn)樗鶎?yīng)的就是具體數(shù)據(jù)。

如果將上面的類稍加修改,看看效果:

#!/usr/bin/env Python
# coding=utf-8

__metaclass__ = type

class Person:
    def __init__(self, name):
        self.name = name
        print self           #新增
        print type(self)     #新增

其它部分省略。當(dāng)初始化的時(shí)候,就首先要運(yùn)行構(gòu)造函數(shù),同時(shí)就打印新增的兩條。結(jié)果是:

<__main__.Person object at 0xb7282cec>
<class '__main__.Person'>

證實(shí)了推理。self 就是一個(gè)實(shí)例(準(zhǔn)確說是實(shí)例的引用變量)。

self 這個(gè)實(shí)例跟前面說的那個(gè) girl 所引用的實(shí)例對象一樣,也有屬性。那么,接下來就規(guī)定其屬性和屬性對應(yīng)的數(shù)據(jù)。上面代碼中:

self.name = name

就是規(guī)定了 self 實(shí)例的一個(gè)屬性,這個(gè)屬性的名字也叫做 name,這個(gè)屬性的值等于初始化函數(shù)的參數(shù) name 所導(dǎo)入的數(shù)據(jù)。注意,self.name 中的 name 和初始化函數(shù)的參數(shù) name 沒有任何關(guān)系,它們兩個(gè)一樣,只不過是一種起巧合(經(jīng)常巧合,其實(shí)是為了省事和以后識別方便,故意讓它們巧合。),或者說是寫代碼的人懶惰,不想另外取名字而已,無他。當(dāng)然,如果寫成 self.xxxooo = name,也是可以的。

其實(shí),從效果的角度來理解,這么理解更簡化:類的實(shí)例 girl 對應(yīng)著 self,girl 通過 self 導(dǎo)入實(shí)例屬性的所有數(shù)據(jù)。

當(dāng)然,self 的屬性數(shù)據(jù),也不一定非得是由參數(shù)傳入的,也可以在構(gòu)造函數(shù)中自己設(shè)定。比如:

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

__metaclass__ = type

class Person:
    def __init__(self, name):
        self.name = name
        self.email = "qiwsir@gmail.com"     #這個(gè)屬性不是通過參數(shù)傳入的

info = Person("qiwsir")              #換個(gè)字符串和實(shí)例化變量
print "info.name=",info.name
print "info.email=",info.email      #info 通過 self 建立實(shí)例,并導(dǎo)入實(shí)例屬性數(shù)據(jù)

運(yùn)行結(jié)果

info.name= qiwsir
info.email= qiwsir@gmail.com    #打印結(jié)果

通過這個(gè)例子,其實(shí)讓我們拓展了對 self 的認(rèn)識,也就是它不僅僅是為了在類內(nèi)部傳遞參數(shù)導(dǎo)入的數(shù)據(jù),還能在初始化函數(shù)中,通過 self.attribute 的方式,規(guī)定 self 實(shí)例對象的屬性,這個(gè)屬性也是類實(shí)例化對象的屬性,即做為類通過初始化函數(shù)初始化后所具有的屬性。所以在實(shí)例 info 中,通過 info.email 同樣能夠得到該屬性的數(shù)據(jù)。在這里,就可以把 self 形象地理解為“內(nèi)外兼修”了?;蛘甙凑涨懊嫠岬降?,將 info 和 self 對應(yīng)起來,self 主內(nèi),info 主外。

怎么樣?是不是明白了類的奧妙?


總目錄   |   上節(jié):類(1)   |   下節(jié):類(3)

如果你認(rèn)為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。