Python 中的引用和類別屬性的理解

大家讲道理
發布: 2016-11-07 17:18:11
原創
1054 人瀏覽過

最近對Python 的物件引用機制稍微研究了一下,留下筆記,以便查閱。

首先有一點是明確的:「Python 中一切皆物件」。

那麼,這到底代表什麼呢?

如下碼:

#!/usr/bin/env python
a = [0, 1, 2] # 来个简单的list
# 最初,list 和其中各个元素的id 是这样的。
print 'origin'
print id(a),a
for x in a:
    print id(x), x
print '----------------------'
# 我们把第一个元素改改
print 'after change a[0]'
a[0] = 4
print id(a),a
for x in a:
    print id(x), x
print '----------------------'
# 我们再把第二个元素改改
print 'after change a[1]'
a[1] = 5
print id(a),a
for x in a:
    print id(x), x
print '----------------------'
# 回头看看直接写个0 ,id是多少
print 'how about const 0?'
print id(0), 0
登入後複製

運作結果如下: 

   

PastgiftMacbookPro:python pastgift$ ./refTPastgiftMacbookPro:python pastgift$ 1, 2]

4298181328 0

4298181304 1

4298181280 2

----------------------

after change a[0]

4299760200 [4, 1, 2]

4298181304 1

4298181280 2

----------------------

after change a[1]

429976020025,

4298181232 4

4298181208 5

4298181280 2

----------------------

how about 2

----------------------

how about 2

238 

從「Origin」部分來看,list 中各個元素的位址之間都正好相差24,依序指向各自的資料——這讓我想到了陣列。

當修改a[0] 的值之後,發現,a[0] 的位址發生了變化。也就是說,賦值語句其實只是讓a[0] 重新指向另一個物件而已。此外,也注意到,a[0] 的地址和a[2]的地址相差48(2個24)。

當再次修改a[1] 之後,同樣地,a[1] 的地址也發生變化,有趣的是,這次a[1] 的地址和a[0] 的地址又相差24,和原先的a[2] 相差72(3個24)。

最後,當直接把數字0的地址印出來後,發現它的地址和最開始的a[0] 的地址完全一樣。

至此,基本上可以說明,就算是list 中的元素,其實也是引用。修改list 中的元素,其實還是在修改引用而已。

對於Python 中類別屬性,有人提到「類別屬性在同一類別及其子類別之間共享,修改類別屬性會影響到同一類別及其子類別的所有物件」。

聽著挺嚇人,但仔細研究之後,其實倒也不是什麼大不了的事。

如下程式碼:

#!/usr/bin/env python
class Bird(object):
    name = 'bird'
    talent = ['fly']
class Chicken(Bird):
    pass
bird = Bird();
bird2 = Bird(); # 同类实例
chicken = Chicken(); # 子类实例
# 最开始是这样的
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 '----------------------------'
# 换个名字看看
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 '----------------------------'
# 洗个天赋试试(修改类属性中的元素)
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 '----------------------------'
# 换个新天赋树(整个类属性全换掉)
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 '----------------------------'
# 洗掉新天赋树(对新来的类属性中的元素进行修改)
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 '----------------------------'
登入後複製

   

運作結果:

Original attr


4301998000 bird

4301857352 ['fly']

4301998000 bird

4301857352 ['fly']

4301998000 bird

430185735225735252] ----

after changing name

4301986984 bird name changed!

4301857352 ['fly']

43019980075

4301998000 bird

4301857352 ['fly']

----------------------------

after changing talent(a list)

4301986984 bird name changed!

4301857352 ['walk ']

4301998000 bird

4301857352 ['walk']

4301998000 bird

430185735225252-- ------

after reassign talent

4301986984 bird name changed!

4301859512 ['swim']

4301859512 ['swim']

4301998055

4301998000 bird

4301857352 ['walk']

----------------------------

changing element after reassigning talent

4301986984 bird name changed!

4301859512 name changed!

4301859512 [d ']

4301998000 bird

4301857352 ['walk']

4301998000 bird

430185735225252-- ------

在「Origin」的時候,同類對象,子類別對象的相同類別屬性的位址都是相同的-這就是所謂的「共享」。

修改name 之後,只有被修改的物件name 屬性會改變。這是因為對name的賦值運算其實就是換了一個字串,重新引用。字串本身並沒有改變。所以並沒有在同類和子類之間產生互相影響。

接下來,修改talent 中的元素。這時,情況有所改變:同類及其子類別的talent 屬性都一起跟著變了——這很好理解,因為它們都引用的記憶體位址都一樣,引用的是同一個物件。

再接下來,給talent 重新賦值,也就是改成引用另外一個物件。結果是只有本實例的talent 屬性變化了。從記憶體位址可以看出,本實例和其他實例的talent 屬性已經不再指向相同的物件了。就是說「至此,本實例已經是圈外人士了」。

那麼,最後再次修改talent 中元素後,對其他實例無影響的結果也是很好理解了。因為已經是「圈外人士」了嘛,我再怎麼折騰也都是自己的事情了。

所以,「类属性在同类及其子类之间互相影响」必须有一个前提条件:实例建立后,其类属性从来没有被重新赋值过,即类属性依然指向最初所指向的内存地址。

最后提一下对象属性

如下代码:


#!/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 '--------------------'
# 修改其中一个对象的属性
bird.talent[0] = 'walk'
print 'after changing attribute'
print id(bird.talent), bird.talent
print id(bird2.talent), bird2.talent
print '--------------------'
# 作死:两个对象的属性指向同一个内存地址,再修改
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 '--------------------'
登入後複製

运行结果:

PastgiftMacbookPro:python pastgift$ ./changeAttributeTest2.py 

Origin

4299867632 ['fly']

4299760200 ['fly']

--------------------

after changing attribute

4299867632 ['walk']

4299760200 ['fly']

--------------------

assign to another attribute and change it

4299760200 ['swim']

4299760200 ['swim']

--------------------

   

由于对象属性就算内容完全一样(刚初始化后的属性内容一般都是一样的),也会分配到完全不同的内存地址上去。所以不存在「同类对象之间影响」的情况。

但如果让一个对象的属性和另一个对象的属性指向同一个地址,两者之间(但也仅限两者之间)便又互相牵连起来。


來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!