一篇文章带你彻底了解python的单例模式

什么是单例模式?
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场

单例模式的应用场景有哪些?
    python 的logger就是一个单例模式,用以日志记录
    Windows 的资源管理器是一个单例模式
    线程池,数据库连接池一般也用单例模式
    网站计数
从这些应用场景我们可以总结以下情况需要单例模式
    当每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,
    它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次;当有同步需要的时候,
    可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,
    这种情况下由于只有一个实例,所以不用担心同步问题。

单例模式的实现方法:

1 使用模块:python的模块是天然的单例模式,因为模块在第一次导入的时候,
会生成 .pyc文件,当第二次导入时,就会直接加载 .pyc文件,而不会再次执行模块代码。因此,
我们只需要把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

eg:
    class Singleton(object):
    def foo(self):
        print('test')
        ...


    singleton = Singleton()

将上面代码保存在文件mysingleton.py中 使用时,直接在其其他文件中倒入此文件中的对象,这个对象即是单例模式的对象

eg:
    from mysingleton import singleton

2 使用装饰器
装饰器的外层变量定义一个字典,里面存放这个类的实例。当第一次创建的时候就将这个实例保存到字典当中
然后每次创建的时候都去字典中判断一下,如果已经被实例化,就直接取这个实例化对象,如果不存在就保存在字典中

eg:
def Singleton(cls):
    _instance = {}

    def _singleton(*args, **kwargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kwargs)
        return _instance[cls]

    return _singleton


@Singleton
class A(object):
    a = 1

    def __init__(self, x):
        # print(x)
        self.x = x

    def get_x(self):
        # print(self.x)
        return self.x


a1 = A(2)
a2 = A(3)

print(id(a1), a1.get_x())
print(id(a2), a2.get_x())

结果:
140370543801064 2
140370543801064 2

不加装饰器:
140651110265576 2
140651110267648 3

3 使用类:
思路就是,调用类的instance方法,这样有一个弊端就是在使用类创建的时候
并不是单例,也就是说创建类的时候一定要用类里面规定的方法创建
eg:
class Singleton(object):
    def __init__(self, *args, **kwargs):
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        # 利用反射,看看这个类有没有_instance 属性
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance


s1 = Singleton() #使用这种方法创建实例的时候,并不能保证是单例
s2 = Singleton.instance() #只有使用这种方式创建的时候,才可以实现单例
s3 = Singleton()
s4 = Singleton.instance()

print(id(s1),id(s2),id(s3),id(s4),sep='
')

结果:
140721137283816
140721137285888
140721161781936
140721161864080

注意,这样的单例模式在单线程下是安全的,但是如果遇到多线程,就会出现问题
如遇到多个线程同时创建这个类的实例的时候就会出现问题

import threading
import time

class Singleton(object):
    def __init__(self, *args, **kwargs):
        time.sleep(1)
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        # 利用反射,看看这个类有没有_instance 属性
        if not hasattr(Singleton, '_instance'):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance


def task(arg):
    obj = Singleton.instance(arg)
    print(obj)

for i in range(10):
    t=threading.Thread(target=task,args=[i,])

    t.start()
结果1:
<__main__.Singleton object at 0x7f8f9ebd8358>
<__main__.Singleton object at 0x7f8f9d76f2e8>
<__main__.Singleton object at 0x7f8f9ee52160>
<__main__.Singleton object at 0x7f8f9ebd8358>
<__main__.Singleton object at 0x7f8fa22964a8>
<__main__.Singleton object at 0x7f8f9ee55cf8>
<__main__.Singleton object at 0x7f8f9d76f2e8>
<__main__.Singleton object at 0x7f8f9ee61e80>
<__main__.Singleton object at 0x7f8f9ee55cf8>
<__main__.Singleton object at 0x7f8fa2296978>


结果2
<__main__.Singleton object at 0x7fc9dc75f4a8>
<__main__.Singleton object at 0x7fc9daad8358>
<__main__.Singleton object at 0x7fc9dc75fa20>
<__main__.Singleton object at 0x7fc9dc75a8d0>
<__main__.Singleton object at 0x7fc9dc75afd0>
<__main__.Singleton object at 0x7fc9dc75aeb8>
<__main__.Singleton object at 0x7fc9dc75abe0>
<__main__.Singleton object at 0x7fc9dc75aac8>
<__main__.Singleton object at 0x7fc9dc75f208>
<__main__.Singleton object at 0x7fc9dc75f0b8>
结果1 是不加休眠时间的多线程执行结果,结果不是特别明显,但是在init
方法中有阻塞,就可以看到非常明显的结果,结果2创建了10个不同的实例对象,这是因为一个对象
创建过程中,另外一个对象也创建了,当他进行判断的时候,会先获取_instance 属性
,因为这个时候_instance 属性还不存在,他就会调用init方法,结果就是调用了10次,创建了10
个对象。
如何解决这个问题:
加锁
在哪里加锁呢?在获取对象属性_instance的时候加锁,如果已经有人在获取对象了,其他人如果要获取这个对象,就要等一下
因为前面那个可能是第一次创建对象。
import threading
import time

class Singleton(object):
    _instance_lock=threading.Lock()
    def __init__(self, *args, **kwargs):
        time.sleep(1)
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        # 利用反射,看看这个类有没有_instance 属性
        with Singleton._instance_lock:
            if not hasattr(Singleton, '_instance'):
                Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance


def task(arg):
    obj = Singleton.instance(arg)
    print(obj)

for i in range(10):
    t=threading.Thread(target=task,args=[i,])

    t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)
加锁之后的结果:
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
<__main__.Singleton object at 0x7f8f73dd8358>
这样就差不多了,但是还是有一点小问题,就是当程序执行时,执行了time.sleep(20)后,
下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,
把intance方法,改成下面的这样就行:

import threading
import time

class Singleton(object):
    _instance_lock=threading.Lock()
    def __init__(self, *args, **kwargs):
        time.sleep(1)
        pass

    @classmethod
    def instance(cls, *args, **kwargs):
        # 利用反射,看看这个类有没有_instance 属性
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, '_instance'):
                    Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance


def task(arg):
    obj = Singleton.instance(arg)
    print(obj)
#
for i in range(10):
    t=threading.Thread(target=task,args=[i,])

    t.start()

time.sleep(20)
obj = Singleton.instance()
print(obj)

4 基于__new__方法实现(推荐使用,方便)
通过上面的例子,我们可以知道实现单例时,为了保证线程安全需要在内部加入锁,
我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__)
实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所以我们可以基于这个实现单例模式

import threading

import time

class Singleton(object):
    _instance_lock=threading.Lock()

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton,'_instance'):
            with Singleton._instance_lock:
                if not hasattr(Singleton,'_instance'):
                    Singleton._instance=object.__new__(cls)
        return Singleton._instance

obj1=Singleton()
obj2=Singleton()

print(obj1,obj2,sep='
')

def task(arg):
    obj=Singleton()

    print(obj)

for i in range(10):
    t=threading.Thread(target=task,args=[i,])
    t.start()

结果:
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
<__main__.Singleton object at 0x7fdab0ec4278>
采用这种方法的单例模式,以后实例化对象,和平时实例化对象方法一样,
obj=Singleton()

5 基于 metaclass 方法实现

类由type创建,创建类时,type的__init__ 方法自动执行,类()执行type的
__call__方法(类的__new__方法,类的__init__方法)
对象由类创建,创建对象时,类的__init__方法自动执行,对象()执行类的__call__方法
例子:

class Foo(object):
    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        pass


obj = Foo()
# 执行type的__call__方法,调用Foo类(是type的对象)的__new__方法,用于创建
# 对象,然后调用 Foo类(是type的对象)的 __init__方法,用于对对象初始化。
obj()  # 执行Foo的 __call__ 方法


# 元类的使用

class SingletonType(type):
    def __init__(self, *args, **kwargs):
        super(SingletonType, self).__init__(*args, **kwargs)

    def __call__(cls, *args, **kwargs):
        obj = cls.__new__(cls, *args, **kwargs) #这里的cls,即Foo类

        cls.__init__(obj, *args, **kwargs)
        return obj


class Foo(metaclass=SingletonType): #指定创建Foo的type为SingletonType

    def __init__(self,name):
        self.name=name

    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)
obj=Foo('xx')

基于这种方法实现的单例:
import threading


class SingletonType(type):
    _instance_lock = threading.Lock()

    def __call__(cls, *args, **kwargs):
        if not hasattr(cls, '_instance'):
            with SingletonType._instance_lock:
                if not hasattr(cls, '_instance'):
                    cls._instance = super(SingletonType, cls).__call__(*args, **kwargs)
        return cls._instance


class Foo(metaclass=SingletonType):

    def __init__(self,name):
        self.name = name


obj1=Foo('name')

obj2=Foo('hello')

print(obj1,obj2,sep='
')

结果:
<__main__.Foo object at 0x7fe5675d8390>
<__main__.Foo object at 0x7fe5675d8390>

本博客借鉴博客地址:https://www.cnblogs.com/huchong/p/8244279.html ,对博客内容进行验证之后发布,希望对大家理解单例模式提供帮助。

原文地址:https://www.cnblogs.com/liangliangzz/p/14302274.html