一文详解“单例模式”及其python语言的实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_27825451/article/details/84135466

一、什么是“单例模式”——一个实例

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

单例模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。

关键实现思想

(1)保证构造函数是私有的(比如java、C++、C#)语言,像python这种没办法构造私有函数的怎么办呢?后面会讲到它灵活的实现。

(2)第一次创建类的对象的时候判断系统是否已经有这个单例,如果有则返回,如果没有则创建。那么后续再次创建该类的实例的时候,因为已经创建过一次了,就不能再创建新的实例了,否则就不是单例啦,直接返回前面返回的实例即可。

二、为什么要实现单例模式呢?

常见的单例模式:

(1)比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。

(2)当有同步需要的时候,可以通过一个实例来进行同步控制,比如对某个共享文件(如日志文件)的控制,对计数器的同步控制等,这种情况下由于只有一个实例,所以不用担心同步问题。

(3)Python的logger就是一个单例模式,用以日志记录;Windows的资源管理器是一个单例模式;线程池,数据库连接池等资源池一般也用单例模式;网站计数器等等,这些都是单例模式。

需要使用单例模式的情景:——这个很重要哦

当每个实例都会占用资源,而且实例初始化会影响性能,这个时候就可以考虑使用单例模式,它给我们带来的好处是只有一个实例占用资源,并且只需初始化一次;归纳为以下几条:

1、要求生产唯一序列号。

2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。

3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。

优缺点:

优点: 1、在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。 2、避免对资源的多重占用(比如写文件操作)。

缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

三、单例模式的python实现

不同的编程语言对于单例模式的实现有所不同,因为每一种语言的语法有所差异,但是所遵循的核心原理以及所要达到的最终目的是一样的,不一样的是,往目的地所走的路不一样而已。python实现单例模式的方法有很多,本文着重讲解三种。

1、首先看一下普通的类——即非单例类

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age



s1=Student('张三',23)
s2=Student('张三',23)

print((s1==s2))
print(s1 is s2)
print(id(s1),id(s2),sep='   ')

运行结果如下:

False
False
2215920481224   2215920481112

从上面可以看出,在非单例模式下,两个实例是完全不一样的。

2、使用“装饰器”来实现单例模式

def Singleton(cls):   #这是一个函数,目的是要实现一个“装饰器”,而且是对类型的装饰器
    '''
    cls:表示一个类名,即所要设计的单例类名称,
        因为python一切皆对象,故而类名同样可以作为参数传递
    '''
    instance = {}

    def singleton(*args, **kargs):
        if cls not in instance:
            instance[cls] = cls(*args, **kargs)   #如果没有cls这个类,则创建,并且将这个cls所创建的实例,保存在一个字典中
        return instance[cls]

    return singleton


@Singleton
class Student(object):
    def __init__(self, name,age):
        self.name=name
        self.age=age

s1 = Student('张三',23)
s2 = Student('李四',24)
print((s1==s2))
print(s1 is s2)
print(id(s1),id(s2),sep='   ')

运行结果为:

True
True
2150787003840   2150787003840

从上面可以看出,虽然创建的两个对象s1 s2看起来是不一样的,但是实际上都是一样的,这就是“单例模式”

3、通过__new__函数去实现

class Student(object):
    instance = None
    def __new__(cls, name,age):
        if not cls.instance:
            cls.instance = super(Student, cls).__new__(cls)  
        return cls.instance  

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


s1 = Student('张三',23)
s2 = Student('李四',24)
print((s1==s2))
print(s1 is s2)
print(id(s1),id(s2),sep='   ')

运行结果如下:

True
True
1718008231696   1718008231696

从上面可见,依然是实现了单例模式。

总结:上面这两种方法,都是python实现单例模式最常用的方法,通过比较发现,这两种方法都有一个共同点,那就是都是通过一个内部变量instance作为桥梁,如果它不是空,就创建一个类的实力绑定到它上面,后续它不为空了,会一直都是这个绑定的对象,这就实现了单例模式。

4、使用一个单独的模块作为单例模式

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

在一个模块中定义一个普通的类,如在Singleton_Pattern.py模块中定义如下代码

class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age

student=Student('张三',23)

这里的student就是一个单例。当我们在另外一个模块中导入student这个对象时,因为它只被导入了一次,所以总是同一个实例。

当然python的单例模式非常的灵活多变,这也是因为Python语言的灵活性所决定的,我们还可以使用元类创建单例模式,甚至还有很多其他的方式都可以创家门单例模式,只要记住单例模式的本质即可。推荐使用前面的两种方法,即装饰器方法和__new__
方法
。因为原理清晰简单,很容易理解。

猜你喜欢

转载自blog.csdn.net/qq_27825451/article/details/84135466