在我的一个业务逻辑代码中,我不幸需要编写以下类(这里我们用抽象的A, B, C表示)
class A:
value = 0
def __init__(self, b):
self.b = b
def set_1(self):
self.value = 1
class B:
def __init__(self):
pass
def reset(self):
self.a = A(self)
这是两个没有什么问题的对象,不过在创建B的实例时,会让自己的属性a指向一个A的实例,而a中的属性b又指向该B实例
所以我们可以像下面这样访问:
b = B()
a = b.a
assert b.a.b is b
assert b.a.b.a is a
这样能够使得b拥有a的控制权,也同时使得a拥有b的控制权。
但是当我试图用一个类C来代理实例b时,却犯了一个错误
class C:
b = B()
a = b.a
def __init__(self):
pass
def read_a(self):
return self.b.a.value
c = C()
assert c.read_a() == 0
c.a.set_1()
assert c.read_a() == 1
c.b.reset()
assert c.read_a() == 0
assert c.a == 0 # AssertionError
为什么这里我会脑子抽断言 c.a == 0呢?
因为我主观地认为这里实例b执行了reset函数,于是重新实例化了属性b.a=A(self)
那么a在实例过程中,会让a.value = 0
所以 c.b.a.value == 0
而我们的c.a = b.a
所以c.a.value == c.b.a.value == 0
几乎完美的推理!!!
但是我忽略了,这里的c.a is not c.b.a !!!
原因如下:
假设一开始实例化A, B 为 a1, b1
c.a -> a1
c.b -> b1
那么当b1.reset() 的时候,a是重新实例化的一个A对象,用JAVA的话来说,就是重新new 了一个A
b1.a -> a2
故c.b.a == c.b1.a == a2
而c.a == a1
a1.value在a1.set_1()之后,赋值为 1
a2.value在b1.reset()之后,赋值为 0
所以断言错误
记录这个错误警告自己在今后的编码过程中不要一味主观地用数学观点,而要弄清楚具体对象。