有一个大笨猪,按java写观察者模式,java写得是直接在类名下声明一个实例属性(不加static的),他直接翻译成python后,也是直接写在类名下面,这就是大坑了。
java里面在类名下面声明的带static为类属性,不带static为实例属性,python如果直接写在类名下面的属性,一律都是类属性,实例属性一般都是在init里面写的,带self字眼的变量。
#coding:utf-8 class A(): x_list = [] a1 = A() a1.x_list.append(1) a2 = A() print(‘第一个结果 -->‘,a2.x_list) class B(): x = 0 b1 = B() b1.x =1 b2 = B() print(‘第二个结果‘,b2.x) class C(): x = 0 c1 = C() C.x =1 c2 = C() print(‘第二个结果‘,c2.x)
运行结果是这样的。
对比 A B C三个类可以发现,类属性自己一份,实例属性默认等于类属性。
比如B类,实例b1的x默认等于0,当赋值为1时候,b1的x就等于1了,此时B类的属性x仍然等于0,b2实例的属性x也是等于0。
c类,直接改变C类的x属性,则C的x变为1了,如果实例化一个新的c2,默认就等于1了。
B和C的这种操作是很多案例上都有的,烂大街了,一定需要查看常见容易错误的案例。
最主要还是A类,A类的x_list是一个列表,列表是可变数据结构,用可变次数据结构作为类属性,就要很小心了,即使是用实例a1操作x_list,仍然会影响A的属性x_list和a2的属性x_list。当可变元素作为类属性时候,操作 a.x_list和A.x_list是等效的,都是互相影响的。
不可变数据结构例子:
a_str = ‘hello’
b_str = a_str
b_str = a_str + ‘world‘
最终a的值是hello ,b的值是hello world。
可变的数据结构例子:
a_list = [‘hello‘]
b_list = a_list
b_list + = [‘world‘]
最终a_list和b_list的值都是[‘hello‘, ‘world‘]
回到题目就是,在观察者模式中,如果把订阅者添加到类属性x_lsit列表,那么所有的发布者都共享了所有的订阅者。
比如小明、小军订阅了苍老师,小红 小黄订阅了波老师,但苍老师更新电影时候,由于共享了订阅者,导致小红和小黄也收到新片通知了,这显然是个错误。
所以苍老师和波老师的x_list必须是独立的,需要写在__init__里面,写成self.x_list,这样苍老师和波老师的x_list就都是独立的了,添加订阅者到苍老师的列表,波老师的列表不会跟着变化。