ホームページ  >  記事  >  バックエンド開発  >  Python の参照とクラス属性を理解する

Python の参照とクラス属性を理解する

高洛峰
高洛峰オリジナル
2016-10-18 09:56:29982ブラウズ

最近、Python のオブジェクト参照メカニズムについて少し調べて、参考のためにメモを残しました。

まず、明らかなことが 1 つあります。「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$ ./refTest.py 
Origin
4299760200 [0, 1, 2]
4298181328 0
4298181304 1
4298181280 2
----------------------
after change a[0]
4299760200 [4, 1, 2]
4298181232 4
4298181304 1
4298181280 2
----------------------
after change a[1]
4299760200 [4, 5, 2]
4298181232 4
4298181208 5
4298181280 2
----------------------
how about const 0?
4298181328 0

「Origin」部分から、リスト内の各要素のアドレスは互いに正確に 24 異なります。それぞれのデータを順番に指す - — これにより、配列が登場します。

a[0]の値を変更したところ、a[0]のアドレスが変更されていることが分かりました。言い換えれば、代入ステートメントは実際には、a[0] が再び別のオブジェクトを指すようにするだけです。さらに、a[0] のアドレスと a[2] のアドレスは 48 (24 が 2 つ) 異なることにも注意してください。

a[1] を再度変更すると、a[1] のアドレスも変更されます。興味深いことに、今回は a[1] のアドレスと a[0] のアドレスが 24 異なり、元のアドレスとは異なります。 a[2] は 72 (24 が 3 つ) 異なります。

最後に、数値 0 のアドレスを直接出力したところ、そのアドレスが元の a[0] のアドレスとまったく同じであることがわかりました。

この時点で、リスト内の要素も実際には参照であることが基本的に説明できます。リスト内の要素を変更すると、実際には参照が変更されます。

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 '----------------------------'

実行結果:

PastgiftMacbookPro:python pastgift$ ./changeAttributeTest.py 
Original attr
4301998000 bird
4301857352 ['fly']
4301998000 bird
4301857352 ['fly']
4301998000 bird
4301857352 ['fly']
----------------------------
after changing name
4301986984 bird name changed!
4301857352 ['fly']
4301998000 bird
4301857352 ['fly']
4301998000 bird
4301857352 ['fly']
----------------------------
after changing talent(a list)
4301986984 bird name changed!
4301857352 ['walk']
4301998000 bird
4301857352 ['walk']
4301998000 bird
4301857352 ['walk']
----------------------------
after reassign talent
4301986984 bird name changed!
4301859512 ['swim']
4301998000 bird
4301857352 ['walk']
4301998000 bird
4301857352 ['walk']
----------------------------
changing element after reassigning talent
4301986984 bird name changed!
4301859512 ['dance']
4301998000 bird
4301857352 ['walk']
4301998000 bird
4301857352 ['walk']
----------------------------

「Origin」では、類似オブジェクトとサブクラスオブジェクトの同じクラス属性のアドレスが同じである、これを呼びます。 「共有」。

名前を変更すると、変更されたオブジェクトの name 属性のみが変更されます。これは、name への代入操作が実際には文字列を変更し、それを再引用しているためです。文字列自体は変更されていません。したがって、同じカテゴリとサブカテゴリの間には相互影響はありません。

次に、タレントの要素を変更します。このとき、状況が変わりました。同じクラスとそのサブクラスのタレント属性がすべて一緒に変更されました。これらはすべて同じメモリ アドレスと同じオブジェクトを参照しているため、これは理解しやすいです。

次に、タレントを再割り当てします。つまり、別のオブジェクトを参照するように変更します。その結果、このインスタンスのタレント属性のみが変更されます。メモリ アドレスから、このインスタンスと他のインスタンスのタレント属性が同じオブジェクトを指していないことがわかります。つまり、「この時点で、この例はすでに部外者である」ということです。

そして、最終的にタレントの要素を再度修正すると、他のインスタンスには影響がないことが容易に理解できます。もう「部外者」だから、いくらいじっても、すべて自分のことだ。

したがって、「同じクラスとそのサブクラスの間でクラス属性が相互に影響を与える」には前提条件が必要です。インスタンスの作成後、そのクラス属性は一度も再割り当てされていない、つまり、クラス属性は依然として元々のメモリアドレスを指していることです。と指摘した。

最後にオブジェクトの属性について触れてみましょう

以下のコード:

   
#!/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']
--------------------

オブジェクトの属性は全く同じ内容(初期化後の属性内容は通常同じ)であるため、全く異なる属性に割り当てられます。メモリアドレス。したがって、「類似したオブジェクト間の影響」はありません。

しかし、あるオブジェクトの属性と別のオブジェクトの属性が同じアドレスを指している場合、その 2 つは (ただし 2 つの間でのみ) 相互に関係します。


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。