对于初学者来说,python的类变量(也就是java中的静态变量)和实例变量(也就是属性)有一些很容易混淆的地方,同时对这些特性深入了解有助于理解python的面向对象思想。
类变量与实例变量区分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| >>> class ClassA(object): ... num1 = 1 ... def __init__(self): ... self.num2 = 2
>>> a1 = ClassA() >>> a1.num1 1 >>> ClassA.num1 1 >>> a1.num1 is ClassA.num1 True
>>> a1.num1 = 3 >>> a1.num1 is ClassA.num1 False
>>> a1.__class__.num1 1 >>> a1.__class__.num1 = 4 >>> ClassA.num1 4
>>> a2 = ClassA() >>> a2.num1 4
|
- 这里我们定义了
ClassA
类,它有一个类变量num1
,还有一个实例变量num2
。
- a1是ClassA的一个实例,当我们写下
a1.num1
的时候,实际上是引用类变量num1,因此a1.num1 is ClassA.num1
值为True
。
- 运行
a1.num1 = 3
的时候,其实给a1绑定了一个属性num1,这是动态语言的特性,此时a1.num1 is ClassA.num1
值为False
。
因此我不建议通过实例的名称来引用类变量,这样容易引起混淆,你以为改变了ClassA.num1,其实没有。
这里就扯到了Python的作用域与命名空间,运行a1.num1
的时候,先是在a1自己的命名空间内查找num1,没找到就在所属类的命名空间找,还没有就抛出AttributeError: 'ClassA' object has no attribute 'num3'
.
运行a1.num1 = 3
之后,a1自己的命名空间内找到了num1,就不继续往上查找了。
- 如果此时还想通过a1访问和改变类变量num1,可以通过
a1.__class__.num1
来访问。
以上这些,也适用于staticmethod
和classmethod
。
如果ClassA还有父类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| >>> class FatherI(object): ... num1 = 11
>>> class FatherII(object): ... num1 = 111
>>> class ClassA(FatherI, FatherII): ... num1 = 1 ... def __init__(self): ... self.num2 = 2
>>> a1 = ClassA() >>> a1.__class__ <class 'ClassA'> >>> a1.__class__.__mro__ (<class 'ClassA'>, <class 'FatherI'>, <class 'FatherII'>, <type 'object'>) >>> a1.__class__.__mro__[0].num1 1 >>> a1.__class__.__mro__[1].num1 11 >>> a1.__class__.__mro__[2].num1 111
>>> super(ClassA, a1).num1 11 >>> super(ClassA.__mro__[0], a1).num1 11 >>> super(FatherI, a1).num1 111 >>> super(ClassA.__mro__[1], a1).num1 111 >>> super(FatherII, a1).num1 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'super' object has no attribute 'num1' 'super' object has no attribute 'num1'
|
- 如果ClassA还有父类,而且可能还有多个,想访问父类的类变量可以通过
__mro__
或者super
。
__mro__
属性显示了一个类的继承树,也就是记录了所有属性和方法的查找顺序,由于此处是新式类,mro为广度优先。
由于__mro__
是一个元祖,所以我们可以用__mro__[1]
这种方式来访问继承树树上每个类的属性和方法。
- 使用super也能达到相同的效果,不过稍有不同.
super(ClassA, a1).num1
这个语句的意思其实是,在__mro__
中访问<class 'ClassA'>
后一个类,也就是<class 'FatherI'>
的num1,所以此处值是11