单例模式(Singleton mode):一种常见的软件设计模式,该设计模式主要目的就是确保某一个类中只能有一个实例存在。
如:产生了很多实例,而它们做的事情只是执行发一个邮件的功能。这会特别浪费内存资源,因为频繁创建和销毁实例。
优点:
1、由于单例模式在内存中只有一个实例,减少内存开支,特别是一个对象需要频繁地创建销毁时,而且创建或销毁时性能又无法优化,单例模式就非常明显了
2、单例模式可以避免对资源的多重占用。单例一次只接受一个使用者,如果还要访问使用,需要等待上一个使用完。
缺点:
1、不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2、单例模式没有抽象层,扩展很困难,若要扩展,除了修改代码别无选择
在了解过模块后可以得知,第一次访问模块以后,会生成将一个.pyc文件,将保存第一次访问的状态。而此后的每次访问模块都是直接加载那个.pyc文件。从这一点就可以得知,Python模块是一个纯天然的单例模式。
开始实例:
# test.py
class People:
pass
people = People()
# run.py
from test import people
p1 = people
p2 = people
print(p1 is p2) # is比较的是两个对象的内存地址,如果相同,则使用的是同一个实例
执行结果
True
def Signleton(cls):
instance = False # 做一个标识,用于第一次返回实例
def inner(*args, **kwargs):
nonlocal instance
if not instance: # 如果为假,则创建一个实例
instance = cls(*args, **kwargs) # 调用People类,产生一个实例
instance.__init__(*args, **kwargs) # 让这个实例的属性可变化
return instance # 返回这个实例
# 当instance拿到实例以后,if将不再执行,所以每次返回的都是第一次产生的实例
return inner
@Signleton
class People: # People = Signleton(People) = inner
def __init__(self, name):
self.name = name
def send_email(self):
print(f'{self.name}发送了邮箱~')
p1 = People('jack') # 执行了inner方法
p1.send_email()
p2 = People('tom')
p2.send_email()
p1.send_email() # 再次执行p1,查看是否保存了状态
print(p1 is p2) # is比较的是两个对象的内存地址,如果相同,则使用的是同一个实例
执行结果
'jack发送了邮箱~'
'tom发送了邮箱~'
'tom发送了邮箱~'
True
可以看到,因为使用的是同一个实例,第二次使用时更改了这个实例的属性。所以再次打印内容也就是最后一次更改的结果
class People:
instance = False # 使用类属性,做一个标识
def __init__(self,name):
self.name = name
@classmethod # 类绑定方法
def get_signleton_obj(cls,*args,**kwargs):
if not cls.instance: # 如果instance为假,则创建一个实例
cls.instance = cls(*args,**kwargs)
# 调用People类,产生一个实例,并赋给instance属性
cls.instance.__init__(*args,**kwargs) # 让调用者可以更改实例的属性
return cls.instance
def send_email(self):
print(f'{self.name}发送了邮箱~')
p1 = People.get_signleton_obj('jack')
p1.send_email()
p2 = People.get_signleton_obj('tom')
p2.send_email()
print(p1 is p2) # is比较的是两个对象的内存地址,如果相同,则使用的是同一个实例
'jack发送了邮箱~'
'tom发送了邮箱~'
True
class People:
instance = None # 类属性做一个标识
def __new__(cls, *args, **kwargs):
if not cls.instance: # 如果instance为假,则创建一个实例
cls.instance = super().__new__(cls)
return cls.instance
# 我们不需要手动来调用,因为执行完new以后,会调用init来初始化属性
def __init__(self,name):
self.name = name
def send_email(self):
print(f'{self.name}发送了邮箱~')
p1 = People('jack')
p1.send_email()
p2 = People('tom')
p2.send_email()
print(p1 is p2)
执行结果
'jack发送了邮箱~'
'tom发送了邮箱~'
True
class MyType(type):
instance = False # 元类的属性
def __call__(self, *args, **kwargs):
# 先去自身及父类里面找instance,没有找到该属性则找到元类里面了
if not self.instance:
# 给自身类增加一个属性,该属性值就是一个实例。
self.instance = super().__call__(*args, **kwargs)
# 每次都可以为这个实例设置新的属性
self.instance.__init__(*args, **kwargs)
return self.instance # 返回这个实例
# 因为我们类的属性名设置了instance,所以下一次就会在自身找到,判断为True取反,不执行if,所以每次拿到的都是第一次的实例
class People(metaclass=MyType):
def __init__(self,name):
self.name = name
def send_email(self):
print(f'{self.name}发送了邮箱~')
p = People('jack')
p.send_email()
p1 = People('tom')
p1.send_email()
print(id(p), id(p1)) # 查看两个对象的内存地址,如果相同则是同一个对象
以上就是笔者所能想到使用Python实现单例模式的5种方式
技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请
点赞 收藏+关注
子夜期待您的关注,谢谢支持!