從零學(xué)Python之引用和類屬性的初步理解
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/env python
a = [0, 1, 2] # 來個(gè)簡(jiǎn)單的list
# 最初,list 和其中各個(gè)元素的id 是這樣的。
print 'origin'
print id(a),a
for x in a:
print id(x), x
print '----------------------'
# 我們把第一個(gè)元素改改
print 'after change a[0]'
a[0] = 4
print id(a),a
for x in a:
print id(x), x
print '----------------------'
# 我們?cè)侔训诙€(gè)元素改改
print 'after change a[1]'
a[1] = 5
print id(a),a
for x in a:
print id(x), x
print '----------------------'
# 回頭看看直接寫個(gè)0 ,id是多少
print 'how about const 0?'
print id(0), 0
運(yùn)行結(jié)果如下:
PastgiftMacbookPro:python pastgift$ ./refTest.py
Origin
[0, 1, 2]
0
1
2
----------------------
after change a[0]
[4, 1, 2]
4
1
2
----------------------
after change a[1]
[4, 5, 2]
4
5
2
----------------------
how about const 0?
0
從「Origin」部分來看,list 中各個(gè)元素的地址之間都正好相差24,依次指向各自的數(shù)據(jù)——這讓我想到了數(shù)組。
當(dāng)修改a[0] 的值之后,發(fā)現(xiàn),a[0] 的地址發(fā)生了變化。也就是說,賦值語(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的地址打印出來后,發(fā)現(xiàn)它的地址和最開始的a[0] 的地址完全一樣。
至此,基本可以說明,就算是list 中的元素,其實(shí)也是引用。修改list 中的元素,實(shí)際上還是在修改引用而已。
對(duì)于Python 中類屬性,有人提到過「類屬性在同一類及其子類之間共享,修改類屬性會(huì)影響到同一類及其子類的所有對(duì)象」。
聽著挺嚇人,但仔細(xì)研究之后,其實(shí)倒也不是什么大不了的事情。
如下代碼:
#!/usr/bin/env python
class Bird(object):
name = 'bird'
talent = ['fly']
class Chicken(Bird):
pass
bird = Bird();
bird2 = Bird(); # 同類實(shí)例
chicken = Chicken(); # 子類實(shí)例
# 最開始是這樣的
print 'Original attr'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
# 換個(gè)名字看看
bird.name = 'bird name changed!'
print 'after changing name'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
# 洗個(gè)天賦試試(修改類屬性中的元素)
bird.talent[0] = 'walk'
print 'after changing talent(a list)'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
# 換個(gè)新天賦樹(整個(gè)類屬性全換掉)
bird.talent = ['swim']
print 'after reassign talent'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
# 洗掉新天賦樹(對(duì)新來的類屬性中的元素進(jìn)行修改)
bird.talent[0] = 'dance'
print 'changing element after reassigning talent'
print id(bird.name), bird.name
print id(bird.talent), bird.talent
print id(bird2.name), bird2.name
print id(bird2.talent), bird2.talent
print id(chicken.name), chicken.name
print id(chicken.talent), chicken.talent
print '----------------------------'
運(yùn)行結(jié)果:
PastgiftMacbookPro:python pastgift$ ./changeAttributeTest.py
Original attr
bird
['fly']
bird
['fly']
bird
['fly']
----------------------------
after changing name
bird name changed!
['fly']
bird
['fly']
bird
['fly']
----------------------------
after changing talent(a list)
bird name changed!
['walk']
bird
['walk']
bird
['walk']
----------------------------
after reassign talent
bird name changed!
['swim']
bird
['walk']
bird
['walk']
----------------------------
changing element after reassigning talent
bird name changed!
['dance']
bird
['walk']
bird
['walk']
----------------------------
在「Origin」的時(shí)候,同類對(duì)象,子類對(duì)象的相同類屬性的地址都是相同的——這就是所謂的「共享」。
修改name 之后,只有被修改的對(duì)象name 屬性發(fā)生變化。這是因?yàn)閷?duì)name的賦值操作實(shí)際上就是換了一個(gè)字符串,重新引用。字符串本身并沒有發(fā)生變化。所以并沒有在同類和子類之間產(chǎn)生互相影響。
接下來,修改talent 中的元素。這時(shí),情況有所改變:同類及其子類的talent 屬性都一起跟著變了——這很好理解,因?yàn)樗鼈兌家玫膬?nèi)存地址都一樣,引用的是同一個(gè)對(duì)象。
再接下來,給talent 重新賦值,也就是改成引用另外一個(gè)對(duì)象。結(jié)果是只有本實(shí)例的talent 屬性變化了。從內(nèi)存地址可以看出,本實(shí)例和其他實(shí)例的talent 屬性已經(jīng)不再指向相同的對(duì)象了。就是說「至此,本實(shí)例已經(jīng)是圈外人士了」。
那么,最后再次修改talent 中元素后,對(duì)其他實(shí)例無(wú)影響的結(jié)果也是很好理解了。因?yàn)橐呀?jīng)是「圈外人士」了嘛,我再怎么折騰也都是自己的事情了。
所以,「類屬性在同類及其子類之間互相影響」必須有一個(gè)前提條件:實(shí)例建立后,其類屬性從來沒有被重新賦值過,即類屬性依然指向最初所指向的內(nèi)存地址。
最后提一下對(duì)象屬性
如下代碼:
#!/usr/bin/env python
class Bird(object):
def __init__(self):
self.talent = ['fly']
bird = Bird()
bird2 = Bird()
# 剛開始的情形
print 'Origin'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------'
# 修改其中一個(gè)對(duì)象的屬性
bird.talent[0] = 'walk'
print 'after changing attribute'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------'
# 作死:兩個(gè)對(duì)象的屬性指向同一個(gè)內(nèi)存地址,再修改
bird.talent = bird2.talent
bird.talent[0] = 'swim'
print 'assign to another attribute and change it'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------'
運(yùn)行結(jié)果:
PastgiftMacbookPro:python pastgift$ ./changeAttributeTest2.py
Origin
['fly']
['fly']
--------------------
after changing attribute
['walk']
['fly']
--------------------
assign to another attribute and change it
['swim']
['swim']
--------------------
由于對(duì)象屬性就算內(nèi)容完全一樣(剛初始化后的屬性內(nèi)容一般都是一樣的),也會(huì)分配到完全不同的內(nèi)存地址上去。所以不存在「同類對(duì)象之間影響」的情況。
但如果讓一個(gè)對(duì)象的屬性和另一個(gè)對(duì)象的屬性指向同一個(gè)地址,兩者之間(但也僅限兩者之間)便又互相牽連起來。
相關(guān)文章
運(yùn)用TensorFlow進(jìn)行簡(jiǎn)單實(shí)現(xiàn)線性回歸、梯度下降示例
這篇文章主要介紹了運(yùn)用TensorFlow進(jìn)行簡(jiǎn)單實(shí)現(xiàn)線性回歸、梯度下降示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03python 解決Windows平臺(tái)上路徑有空格的問題
這篇文章主要介紹了python 解決Windows平臺(tái)上路徑有空格的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-11-11簡(jiǎn)單了解django索引的相關(guān)知識(shí)
這篇文章主要介紹了簡(jiǎn)單了解django索引的相關(guān)知識(shí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07淺談Python3中datetime不同時(shí)區(qū)轉(zhuǎn)換介紹與踩坑
最近的項(xiàng)目需要根據(jù)用戶所屬時(shí)區(qū)制定一些特定策略,學(xué)習(xí)、應(yīng)用了若干python3的時(shí)區(qū)轉(zhuǎn)換相關(guān)知識(shí),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08Python統(tǒng)計(jì)字符串中英文字母、空格、數(shù)字和其它字符的個(gè)數(shù)
這篇文章主要給大家介紹了關(guān)于Python統(tǒng)計(jì)字符串中英文字母、空格、數(shù)字和其它字符的個(gè)數(shù)的相關(guān)資料,本文實(shí)例講述了python統(tǒng)計(jì)字符串中指定字符出現(xiàn)次數(shù)的方法,需要的朋友可以參考下2023-06-06Python Mysql數(shù)據(jù)庫(kù)操作 Perl操作Mysql數(shù)據(jù)庫(kù)
python對(duì)mysql數(shù)據(jù)庫(kù)的一些操作實(shí)現(xiàn)代碼2009-01-01解決Django模板無(wú)法使用perms變量問題的方法
這篇文章主要給大家介紹了關(guān)于解決Django模板無(wú)法使用perms變量問題的方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09Python中的復(fù)雜數(shù)據(jù)類型(list、tuple)
這篇文章介紹了Python中的復(fù)雜數(shù)據(jù)類型(list、tuple),文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-05-05