关于类变量和实例变量,我相信很多小白刚开始学习Python的时候,一直没有搞懂里面初始化代码的意思,也没有哪本书介绍过这里面各个参数的意思,这里看完你就会有一个清醒的认识了。

我们先来看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
class A:
aa =1 # 类变量

def __init__(self, x, y): # 这里的self是指这个类的一个实例化对象,不是类本身
self.x = x # 类实例化后的对象的x等于我们传进来的x (self.x属于我们实例化后的对象,不再属于类本身)
self.y = y # 类实例化后的对象的y等于我们传进来的y (self.y属于我们实例化后的对象,不再属于类本身)


a = A(2, 4)
print(a.x, a.y, a.aa)

# 输出结果:
2 4 1

这里的a.x 其实就是self.x,a.y其实就是self.y,而这里的a.aa则是类的aa,为什么会这样呢,那是因为这关系到python变量的查找顺序,后面会说。它会首先查找这个实例化对象是否存在aa这个属性,如果不存在就往上类中进行查找。

我们在初始化中,Pycharm自动添加的这个self是指这个类的一个实例化对象,不是类本身,我相信肯定有人现在才明白这一点,以前都认为是这个类。之后 self.x 也是属于我们实例化后的对象,不再属于类本身,这一点的理解是非常重要的。

我们来看一下,这个输出是正常的:

1
2
3
print(A.aa)

# 输出结果:1

我们再来尝试一下其他的,这个输出就会报错了:

1
2
3
4
5
print(A.x)

# 输出结果:
print(A.x)
AttributeError: type object 'A' has no attribute 'x'

因为Python变量的查找顺序是从低到高,而不会相反,所以就会出问题。(前面说过self.x 是属于我们实例化后的对象,不再属于类本身)

我们再来思考一下,如果我们修改一下类变量,又会怎样呢?肯定所有的引用都会发生改变,这个很好理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A:
aa =1 # 类变量

def __init__(self, x, y):
self.x = x
self.y = y

a = A(2, 4)
A.aa = 100
print(a.x, a.y, a.aa)
print(A.aa)

# 输出结果:
2 4 100
100

如果我们修改了实例变量中对于类变量的引用值呢?结果又会是怎样,来看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A:
aa =1 # 类变量

def __init__(self, x, y):
self.x = x
self.y = y

a = A(2, 4)
A.aa = 100
a.aa = 1000
print(a.x, a.y, a.aa)
print(A.aa)

# 输出结果:
2 4 1000
100

看,是不是很神奇,我们发现通过a.aa = 1000这个操作,使得我们实例化的对象多了一个aa变量,而通过A.aa = 100这个操作只是修改了我们类变量。然后我们调用a.aa的时候,就优先去查找我们实例化对象的变量,如果不存在再去查找类变量。这个a.aa新建的变量是属于a这个实例化对象的,不信你往下看:

1
2
3
4
5
b = A(5, 9)
print(b.aa)

# 输出结果:
100

因为我们之前运行了A.aa = 100,把我们的类变量的值给修改了,所以才会这样。