Pyhon 中类属性与实例属性的区别

来源:互联网 发布:lr软件好用么 编辑:程序博客网 时间:2024/05/16 04:38

根据网上的一些博客和自己的理解,说一下自己的想法,某些地方加了一些自己的理解,某些地方直接摘抄他人的,具体可以直接看例子,如有不准确非常希望大家指正,如有表达不好请大家见谅。

学习地址:dreamfor, 伯乐在线, fanyuchen

情景一:

# -*- coding: utf-8 -*-class A(object):    t = []    t1 = 0        def add(self, c):        print c,'int id',id(self.t),c,'list id', id(self.t1)        self.t1 += 1                self.t.append(1)        print c,'int id',id(self.t),c,'list id', id(self.t1)        print self.t1, self.t    def sss(self, c):        print self.t1, self.ta = A()a1 = a.add('a')print id(a1)a2 = a.add('a')print id(a2)a.add('a')a.sss('a')b = A()b1  = b.add('b')print id(b1)b.add('b’)b.sss('b')a.sss('a')

结果:

a int id 4377846200 a list id 140559058517120a int id 4377846200 a list id 1405590585170961 [1]4410593176a int id 4377846200 a list id 140559058517096a int id 4377846200 a list id 1405590585170722 [1, 1]4410593176a int id 4377846200 a list id 140559058517072a int id 4377846200 a list id 1405590585170483 [1, 1, 1]3 [1, 1, 1]b int id 4377846200 b list id 140559058517120b int id 4377846200 b list id 1405590585170961 [1, 1, 1, 1]b int id 4377846200 b list id 140559058517096b int id 4377846200 b list id 1405590585170722 [1, 1, 1, 1, 1]2 [1, 1, 1, 1, 1]3 [1, 1, 1, 1, 1]

情景二:

class Test():    test = 10# 情形1obj1 = Test()obj2 = Test()print obj1.test, obj2.test, Test.test# 情形2obj1.test += 2print obj1.test, obj2.test, Test.test# 情形3Test.test += 3print obj1.test, obj2.test, Test.test# 情形4obj2.test += 2print obj1.test, obj2.test, Test.test# 情形5Test.test += 3print obj1.test, obj2.test, Test.test

结果:

10 10 1012 10 1012 13 1312 15 1312 15 16

首先看 情景一:
原因是因为 t,t1属性被称为类属性,既然是类属性,那么根据从C++/Java这种静态语言使用的经验来判断,类属性应该是为其实例所共享的。

其中 int类型为不可变对象,可以看到实例方法a每次调用其id一直是一样,但是看最后获取实例a和实例b的int对象时:看到虽然ID相同,但是值不同,而实例方法每次调用的ID相同

也就是说 类属性如果是不可变对象:类属性是被实例所共享的,不可变对象每次修改都是重新赋值, 在对类实例属性为不可变对象进行赋值的时候,实际上根据规则,每次引用或者修改的都是上一级的固定地址

而list类型为可变对象,实例方法a每次调用都改变ID,列表内容增加,而在调用实例b 看到list的ID 又变成了第一次调用实力方法a时的ID

根据Python中属性的获取存在一个向上查找机制, 每一个实例的每次调用,都要记录他的上一级关系,如果根据可变对象内存地址不变是没办法做到的,所以每次调用要创开辟的内存地址

接着看情景二:
Python属于动态强类型的语言,在很多地方和静态语言不同, Python中属性的获取存在一个向上查找机制
Python中一切皆对象,Test属于类对象,obj1属于实例对象,从对象的角度来看,Test与obj1是两个无关的对象,但是,Python通过下面的查找树建立了类对象Test与实例对象obj1、obj2之间的关系。
如图所示

    Test     |  -----  |     |     obj1   obj2

当调用Test.test时,直接从Test获取其属性test。
但是情形1中调用obj1.test时,Python按照从obj1到Test的顺序由下到上查找属性test。
值得注意的这时候obj1是没有属性test的,于是,Python到类Test中去查找,成功找到,并显示出来。所以,从现象上来看,Test的属性test确实是共享给其所有实例的,虽然这里只是从查找树的形式模拟了其关系。
Python中的属性设置

原帖子的作者也指出问题的关键在于情形2中obj1.test += 2。
为什么呢?
上面我们指出test += 2包含了属性获取及属性设置两个操作。即obj1.test+= 2等价于obj1.test = obj1.test + 2。
其中等式右侧的obj.test属于属性获取,其规则是按照上面提到的查找规则进行,即,这时候,获取到的是Test的属性test,所以等式左侧的值为12。
第二个操作是属性设置,即obj.test = 12。当发生属性设置的时候,obj1这个实例对象没有属性test,因此会为自身动态添加一个属性test。
由于从对象的角度,类对象和实例对象属于两个独立的对象,所以,这个test属性只属于obj1,也就是说,这时候类对象Test和实例对象test各自有一个属性test。
那么,在情形3中,再次调用obj1.test时,按照属性调用查找规则,这个时候获取到的是实例对象obj1的属性test,而不是类对象Test的属性test。

原创粉丝点击