简介
单例模式,顾名思义,就是在整个程序中,只生成一个实例,即便是重新显示调用,生成的也是相同的实例!主要用在只需要一个对象的业务场景,且不希望每次都重新创建!简单讲就是一次创建,随处使用。
代码实现
python的单例非常简单,有多种实现方式;
通过get_instance
class SingletonPeople:
"""
通过get_instance实现单例
"""
def __init__(self):
self.count = 0
@staticmethod
def get_instance():
if not hasattr(SingletonPeople, "_instance"):
SingletonPeople._instance = SingletonPeople()
return SingletonPeople._instance
def test_instance_method(self, content):
self.count += 1
print(content)
成员函数get_instance必须是静态方法,通过类调用来创建实例和获取已生成的实例!
if __name__ == '__main__':
people = SingletonPeople.get_instance()
print(id(people))
people.test_instance_method("this is a singleton class implemented by static method get_instance")
print(people.count)
spare = SingletonPeople.get_instance()
print(id(spare))
spare.test_instance_method("this is a spare people")
print(spare.count)
4323587696
this is a singleton class implemented by static method get_instance
1
4323587696
this is a spare people
2
Process finished with exit code 0
通过魔术方法__new__
class SingletonDog:
"""
通过__new__来实现单例
"""
def __init__(self):
if not hasattr(self, "num"):
self.num = 0
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
def test_instance_method(self, content):
self.num += 1
print(content)
get_instance方法简单高效,不过由于python的语言特性,一切皆可访问,如果显式实例化的依然无法避免创建多个实例的情况。那么可以在类新建的时候,保证只生成单个实例。
python中类的创建在魔术方法__new__中,通过__new__创建实例之后,再调用__init__方法执行参数初始化操作。
所以上面的代码,能够保证每次实例化都是同一个类实例。
if __name__ == '__main__':
dog = SingletonDog()
print(id(dog))
dog.test_instance_method("this is a singleton class implemented by static method get_instance")
print(dog.num)
spare = SingletonDog()
print(id(spare))
spare.test_instance_method("this is a spare people")
print(spare.num)
4438931296
this is a singleton class implemented by static method get_instance
1
4438931296
this is a spare people
2
Process finished with exit code 0
有一点需要注意的是,上面的__new__方法中实现的,并不是线程安全的;若需要保证线程安全,比如实现日志单例类,需要加上线程锁
class SingletonDog:
"""
通过__new__来实现单例
"""
lock = Lock()
def __init__(self):
if not hasattr(self, "num"):
self.num = 0
def __new__(cls, *args, **kwargs):
if not hasattr(cls, "_instance"):
with cls.lock:
if not hasattr(cls, "_instance"):
cls._instance = super().__new__(cls, *args, **kwargs)
return cls._instance
def test_instance_method(self, content):
self.num += 1
print(content)
多线程中如果遇到实例创建比较耗时的单例,就会造成创建多次实例的情况!这种就丧失了单例功能。
所以在__new__执行了加锁操作。锁定当前的资源,之所以加了两层hasattr(cls, “_instance”)的判断,是为了防止,多个线程同时执行到with cls.lock语句,导致创建多次实例!
通过__new__方法创建的单例,每次获取实例对象都会调用一次__init__方法,重新初始化一遍实例参数。在类的内部,实例参数相当于全局变量;所以单例的初始化参数需要注意!当然也可以利用这一特性,实现一些功能。