一文秒懂,python实现单例模式

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

单例模式(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__函数去实现

若要得到当前类的实例,应在当前类__new__()中调用其父类的__new__()来返回,python2和python3调用父类的__new__()方法不同:
python2: object.__new__(cls)
python3: super().__new__(cls) #super()中类可加可不加

基于python3代码:

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='   ')

基于python2代码实现

if not cls.instance:
            cls.instance = object.__new__(cls)
        return cls.instance

运行结果如下:

True
True
1718008231696   1718008231696

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

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

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

因为,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。

因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:

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

#demo.py
class Student:
    def __init__(self,name,age):
        self.name=name
        self.age=age
 
student=Student('张三',23)

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

#test.py
from demo import student
#此时,无论该test脚本怎么运行,import进来的student实例是唯一的

当然python的单例模式非常的灵活多变,这也是因为Python语言的灵活性所决定的,我们还可以使用元类创建单例模式,甚至还有很多其他的方式都可以创家门单例模式,只要记住单例模式的本质即可。

推荐使用前面的两种方法,即装饰器方法和__new__方法。因为原理清晰简单,很容易理解。

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Python面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它将数据和操作封装在对象中,通过对象之间的交互实现程序的设计和开发。下面是一些关键概念,帮助你更好地理解Python面向对象编程。 1. 类(Class):类是对象的蓝图或模板,描述了对象的属性和行为。它定义了对象的特征和方法。例如,我们可以定义一个名为"Car"的类来表示汽车,其中包含属性(如颜色、型号)和方法(如加速、刹车)。 2. 对象(Object):对象是类的实例,是具体的实体。通过实例化类,我们可以创建一个对象。例如,我们可以创建一个名为"my_car"的对象,它是基于"Car"类的实例。 3. 属性(Attribute):属性是对象的特征,用于描述对象的状态。每个对象都可以具有一组属性。例如,"Car"类的属性可以包括颜色、型号等。 4. 方法(Method):方法是对象的行为,用于定义对象的操作。每个对象都可以具有一组方法。例如,"Car"类的方法可以包括加速、刹车等。 5. 继承(Inheritance):继承是一种机制,允许我们创建一个新类(称为子类),从现有类(称为父类)继承属性和方法。子类可以扩展或修改父类的功能。继承可以实现代码重用和层次化设计。 6. 多态(Polymorphism):多态是一种特性,允许不同类的对象对同一方法做出不同的响应。多态提高了代码的灵活性和可扩展性。 7. 封装(Encapsulation):封装是一种将数据和操作封装在对象中的机制,隐藏了对象的内部实现细节,只暴露必要的接口给外部使用。这样可以保护数据的安全性,提供了更好的模块化和代码复用性。 通过理解这些概念,你可以更好地掌握Python面向对象编程。在实践中,你可以使用类来创建对象,操作对象的属性和调用对象的方法,通过继承和多态实现代码的灵活性和可扩展性,通过封装保护数据的安全性和提高代码的可维护性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值