從零學(xué)python之引用和類屬性的初步理解

字號(hào):


    python是一種解釋型、面向?qū)ο?、?dòng)態(tài)數(shù)據(jù)類型的高級(jí)程序設(shè)計(jì)語(yǔ)言。自從20世紀(jì)90年代初python語(yǔ)言誕生至今,它逐漸被廣泛應(yīng)用于處理系統(tǒng)管理任務(wù)和web編程。python已經(jīng)成為最受歡迎的程序設(shè)計(jì)語(yǔ)言之一。2011年1月,它被tiobe編程語(yǔ)言排行榜評(píng)為2010年度語(yǔ)言。自從2004年以后,python的使用率是呈線性增長(zhǎng)。
    python在設(shè)計(jì)上堅(jiān)持了清晰劃一的風(fēng)格,這使得python成為一門易讀、易維護(hù),并且被大量用戶所歡迎的、用途廣泛的語(yǔ)言。
    鑒于以上各種優(yōu)點(diǎn),忍不住對(duì)python進(jìn)行了一番學(xué)習(xí),略有收獲,分享給大家。
    最近對(duì)python的對(duì)象引用機(jī)制稍微研究了一下,留下筆記,以供查閱。
    首先有一點(diǎn)是明確的:「python中一切皆對(duì)象」。
    那么,這到底意味著什么呢?
    如下代碼:
    代碼如下:
    #!/usr/bin/envpython
    a=[0,1,2]#來(lái)個(gè)簡(jiǎn)單的list
    #最初,list和其中各個(gè)元素的id是這樣的。
    print'origin'
    printid(a),a
    forxina:
    printid(x),x
    print'----------------------'
    #我們把第一個(gè)元素改改
    print'afterchangea[0]'
    a[0]=4
    printid(a),a
    forxina:
    printid(x),x
    print'----------------------'
    #我們?cè)侔训诙€(gè)元素改改
    print'afterchangea[1]'
    a[1]=5
    printid(a),a
    forxina:
    printid(x),x
    print'----------------------'
    #回頭看看直接寫個(gè)0,id是多少
    print'howaboutconst0?'
    printid(0),0
    運(yùn)行結(jié)果如下:
    代碼如下:
    pastgiftmacbookpro:pythonpastgift$./reftest.py
    origin
    [0,1,2]
    0
    1
    2
    ----------------------
    afterchangea[0]
    [4,1,2]
    4
    1
    2
    ----------------------
    afterchangea[1]
    [4,5,2]
    4
    5
    2
    ----------------------
    howaboutconst0?
    0
    從「origin」部分來(lái)看,list中各個(gè)元素的地址之間都正好相差24,依次指向各自的數(shù)據(jù)——這讓我想到了數(shù)組。
    當(dāng)修改a[0]的值之后,發(fā)現(xiàn),a[0]的地址發(fā)生了變化。也就是說(shuō),賦值語(yǔ)句實(shí)際上只是讓a[0]重新指向另一個(gè)對(duì)象而已。此外,還注意到,a[0]的地址和a[2]的地址相差48(2個(gè)24)。
    當(dāng)再次修改a[1]之后,同樣地,a[1]的地址也發(fā)生變化,有趣的是,這次a[1]的地址和a[0]的地址又相差24,和原先的a[2]相差72(3個(gè)24)。
    最后,當(dāng)直接把數(shù)字0的地址打印出來(lái)后,發(fā)現(xiàn)它的地址和最開(kāi)始的a[0]的地址完全一樣。
    至此,基本可以說(shuō)明,就算是list中的元素,其實(shí)也是引用。修改list中的元素,實(shí)際上還是在修改引用而已。
    對(duì)于python中類屬性,有人提到過(guò)「類屬性在同一類及其子類之間共享,修改類屬性會(huì)影響到同一類及其子類的所有對(duì)象」。
    聽(tīng)著挺嚇人,但仔細(xì)研究之后,其實(shí)倒也不是什么大不了的事情。
    如下代碼:
    代碼如下:
    #!/usr/bin/envpython
    classbird(object):
    name='bird'
    talent=['fly']
    classchicken(bird):
    pass
    bird=bird();
    bird2=bird();#同類實(shí)例
    chicken=chicken();#子類實(shí)例
    #最開(kāi)始是這樣的
    print'originalattr'
    printid(bird.name),bird.name
    printid(bird.talent),bird.talent
    printid(bird2.name),bird2.name
    printid(bird2.talent),bird2.talent
    printid(chicken.name),chicken.name
    printid(chicken.talent),chicken.talent
    print'----------------------------'
    #換個(gè)名字看看
    bird.name='birdnamechanged!'
    print'afterchangingname'
    printid(bird.name),bird.name
    printid(bird.talent),bird.talent
    printid(bird2.name),bird2.name
    printid(bird2.talent),bird2.talent
    printid(chicken.name),chicken.name
    printid(chicken.talent),chicken.talent
    print'----------------------------'
    #洗個(gè)天賦試試(修改類屬性中的元素)
    bird.talent[0]='walk'
    print'afterchangingtalent(alist)'
    printid(bird.name),bird.name
    printid(bird.talent),bird.talent
    printid(bird2.name),bird2.name
    printid(bird2.talent),bird2.talent
    printid(chicken.name),chicken.name
    printid(chicken.talent),chicken.talent
    print'----------------------------'
    #換個(gè)新天賦樹(shù)(整個(gè)類屬性全換掉)
    bird.talent=['swim']
    print'afterreassigntalent'
    printid(bird.name),bird.name
    printid(bird.talent),bird.talent
    printid(bird2.name),bird2.name
    printid(bird2.talent),bird2.talent
    printid(chicken.name),chicken.name
    printid(chicken.talent),chicken.talent
    print'----------------------------'
    #洗掉新天賦樹(shù)(對(duì)新來(lái)的類屬性中的元素進(jìn)行修改)
    bird.talent[0]='dance'
    print'changingelementafterreassigningtalent'
    printid(bird.name),bird.name
    printid(bird.talent),bird.talent
    printid(bird2.name),bird2.name
    printid(bird2.talent),bird2.talent
    printid(chicken.name),chicken.name
    printid(chicken.talent),chicken.talent
    print'----------------------------'
    運(yùn)行結(jié)果:
    代碼如下:
    pastgiftmacbookpro:pythonpastgift$./changeattributetest.py
    originalattr
    bird
    ['fly']
    bird
    ['fly']
    bird
    ['fly']
    ----------------------------
    afterchangingname
    birdnamechanged!
    ['fly']
    bird
    ['fly']
    bird
    ['fly']
    ----------------------------
    afterchangingtalent(alist)
    birdnamechanged!
    ['walk']
    bird
    ['walk']
    bird
    ['walk']
    ----------------------------
    afterreassigntalent
    birdnamechanged!
    ['swim']
    bird
    ['walk']
    bird
    ['walk']
    ----------------------------
    changingelementafterreassigningtalent
    birdnamechanged!
    ['dance']
    bird
    ['walk']
    bird
    ['walk']
    ----------------------------
    在「origin」的時(shí)候,同類對(duì)象,子類對(duì)象的相同類屬性的地址都是相同的——這就是所謂的「共享」。
    修改name之后,只有被修改的對(duì)象name屬性發(fā)生變化。這是因?yàn)閷?duì)name的賦值操作實(shí)際上就是換了一個(gè)字符串,重新引用。字符串本身并沒(méi)有發(fā)生變化。所以并沒(méi)有在同類和子類之間產(chǎn)生互相影響。
    接下來(lái),修改talent中的元素。這時(shí),情況有所改變:同類及其子類的talent屬性都一起跟著變了——這很好理解,因?yàn)樗鼈兌家玫膬?nèi)存地址都一樣,引用的是同一個(gè)對(duì)象。
    再接下來(lái),給talent重新賦值,也就是改成引用另外一個(gè)對(duì)象。結(jié)果是只有本實(shí)例的talent屬性變化了。從內(nèi)存地址可以看出,本實(shí)例和其他實(shí)例的talent屬性已經(jīng)不再指向相同的對(duì)象了。就是說(shuō)「至此,本實(shí)例已經(jīng)是圈外人士了」。
    那么,最后再次修改talent中元素后,對(duì)其他實(shí)例無(wú)影響的結(jié)果也是很好理解了。因?yàn)橐呀?jīng)是「圈外人士」了嘛,我再怎么折騰也都是自己的事情了。
    所以,「類屬性在同類及其子類之間互相影響」必須有一個(gè)前提條件:實(shí)例建立后,其類屬性從來(lái)沒(méi)有被重新賦值過(guò),即類屬性依然指向最初所指向的內(nèi)存地址。
    最后提一下對(duì)象屬性
    如下代碼:
    代碼如下:
    #!/usr/bin/envpython
    classbird(object):
    def__init__(self):
    self.talent=['fly']
    bird=bird()
    bird2=bird()
    #剛開(kāi)始的情形
    print'origin'
    printid(bird.talent),bird.talent
    printid(bird2.talent),bird2.talent
    print'--------------------'
    #修改其中一個(gè)對(duì)象的屬性
    bird.talent[0]='walk'
    print'afterchangingattribute'
    printid(bird.talent),bird.talent
    printid(bird2.talent),bird2.talent
    print'--------------------'
    #作死:兩個(gè)對(duì)象的屬性指向同一個(gè)內(nèi)存地址,再修改
    bird.talent=bird2.talent
    bird.talent[0]='swim'
    print'assigntoanotherattributeandchangeit'
    printid(bird.talent),bird.talent
    printid(bird2.talent),bird2.talent
    print'--------------------'
    運(yùn)行結(jié)果:
    代碼如下:
    pastgiftmacbookpro:pythonpastgift$./changeattributetest2.py
    origin
    ['fly']
    ['fly']
    --------------------
    afterchangingattribute
    ['walk']
    ['fly']
    --------------------
    assigntoanotherattributeandchangeit
    ['swim']
    ['swim']
    --------------------
    由于對(duì)象屬性就算內(nèi)容完全一樣(剛初始化后的屬性內(nèi)容一般都是一樣的),也會(huì)分配到完全不同的內(nèi)存地址上去。所以不存在「同類對(duì)象之間影響」的情況。
    但如果讓一個(gè)對(duì)象的屬性和另一個(gè)對(duì)象的屬性指向同一個(gè)地址,兩者之間(但也僅限兩者之間)便又互相牽連起來(lái)。