单例模式在模块内调用时,内存地址是相同的。但是我在模块之间调用时,发现了地址不同的情况,从而导致代码逻辑混乱。将自己踩过的坑记录在次,供以后查阅,也希望能让同级别的初学者少走点弯路。不想费时间看过程的,可以直接看最后的总结。
1.只用1个模块,正常
先记下模块内调用,以证明代码没问题。过程是 实例化Singleton,然后通过instantiation方法第2次实例化,验证两次实例化的地址是否相同。
#!python3
# coding:utf-8
class Singleton:
obj = None
def __new__(cls, *args, **kwargs):
if Singleton.obj is None:
Singleton.obj = super().__new__(cls)
return Singleton.obj
def instantiation():
s2 = Singleton()
print(f'第2次调用地址:{s2}')
if __name__ == '__main__':
s1 = Singleton()
print(f'第1次调用地址:{s1}')
instantiation()
第1次调用地址:<__main__.Singleton object at 0x00000215047388B0>
第2次调用地址:<__main__.Singleton object at 0x00000215047388B0>
2_1.用2个模块,从单例模块运行,出问题
做成两个模块进行互相导入后再调用,从module1运行。注意,这会出现不同的地址。
#!python3
# coding:utf-8
# FileName: module1
import module2
class Singleton:
obj = None
def __new__(cls, *args, **kwargs):
if Singleton.obj is None:
Singleton.obj = super().__new__(cls)
return Singleton.obj
if __name__ == '__main__':
s1 = Singleton()
print(f'第1次调用地址:{s1}')
module2.instantiation()
#!python3
# coding:utf-8
# FileName: module2
import module1
def instantiation():
s2 = module1.Singleton()
print(f'第2次调用地址:{s2}')
if __name__ == '__main__':
pass
第1次调用地址:<__main__.Singleton object at 0x00000158350A98B0>
第2次调用地址:<module1.Singleton object at 0x00000158350EB6D0>
我不知道为什么会这样,琢磨了很久,感觉代码逻辑没问题,就是会出现2个内存地址。
2_2.用2个模块,从非单例模块运行,正常
尝试从module2运行。地址相同。
#!python3
# coding:utf-8
# FileName: module1
class Singleton:
obj = None
def __new__(cls, *args, **kwargs):
if Singleton.obj is None:
Singleton.obj = super().__new__(cls)
return Singleton.obj
if __name__ == '__main__':
pass
#!python3
# coding:utf-8
# FileName: module2
import module1
def instantiation():
s2 = module1.Singleton()
print(f'第2次调用地址:{s2}')
if __name__ == '__main__':
s1 = module1.Singleton()
print(f'第1次调用地址:{s1}')
instantiation()
第1次调用地址:<module1.Singleton object at 0x000001C9E2199670>
第2次调用地址:<module1.Singleton object at 0x000001C9E2199670>
3.用3个模块,正常
尝试创建第3个模块,导入前2个模块,从第3个模块运行,发现地址是一样的,真是百思不得姐。
#!python3
# coding:utf-8
# FileName: module3
import module1
import module2
if __name__ == '__main__':
s1 = module1.Singleton()
print(f'第1次调用地址:{s1}')
module2.instantiation()
第1次调用地址:<module1.Singleton object at 0x000002C55BE69670>
第2次调用地址:<module1.Singleton object at 0x000002C55BE69670>
4.分析
对比后发现确实有区别,正常的结果中,要么全部是“__main__.Singleton...”,要么全部是“module1.Singleton...”。但2_1却各占一样。
我的理解是“__main__.Singleton”是本模块内运行,既if __name__ == '__main__':之后的运行,是本模块内部的类实例化的。而“module1.Singleton”是导入模块运行,是从外部模块导入之后实例化的。
感觉:class 从本模块实例化用的"是一个内存空间(自己瞎猜的)",而被别的模块导入后实例化用的是另一个内存空间,不同内存空间之间不能共享数据,所以单例的class就像平行宇宙一样出现不同的内存地址。
5.总结:
a.以后使用单例模式时,如果不牵涉模块之间调用,则不会出问题。
b.如果牵涉到模块之间调用,则最好将单例class做成被调用的模块(既:不要从含有单例class的模块运行)。
6.迷惑之处
虽然分析到了区别,但这个区别为何会导致内存地址不同?如有前辈看到,还请解惑,不胜感激!