【设计模式】单例模式--Python实现单例--线程安全的单例

本文深入解析单例模式的概念、应用场景及实现原理,包括模块、装饰器、懒汉式非线程安全与线程安全四种代码实现方式,适用于资源共享、控制资源、网站计数器等场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、单例模式概念

单例模式(Singleton Pattern) 是最简单的设计模式之一,属于创建型模式。它提供了一种创建对象的最佳方式。

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

注意:

  • 1、单例类只能有一个实例。
  • 2、单例类必须自己创建自己的唯一实例。
  • 3、单例类必须给所有其他对象提供这一实例。
  • 每一次执行 类名() 返回的对象,内存地址是相同

优点:

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

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

二、应用场景
  1. 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如日志文件,应用配置。

  2. 控制资源的情况下,方便资源之间的互相通信。如线程池等。

网站的计数器 不用每次刷新都在数据库里加一次,用单例先缓存起来。
应用配置
多线程池
数据库配置,数据库连接池 创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
应用程序的日志应用
要求生产唯一序列号
音乐播放 对象
回收站 对象
打印机 对象

三、实现核心原理

重写类方法中的__new__方法

  • 使用 类名() 创建对象时,Python 的解释器 首先 会 调用 __new__ 方法为对象 分配空间
  • __new__ 是一个 由 object 基类提供的 内置的静态方法,主要作用有两个:
      1. 在内存中为对象 分配空间
      1. 返回 对象的引用
  • Python 的解释器获得对象的 引用 后,将引用作为 第一个参数,传递给 __init__ 方法

重写 __new__ 方法 的代码非常固定!

  • 重写 __new__ 方法 一定要 return super().__new__(cls)
  • 否则 Python 的解释器 得不到 分配了空间的 对象引用就不会调用对象的初始化方法
  • 注意:__new__ 是一个静态方法,在调用时需要 主动传递 cls 参数
四、单例模式代码实现方式
4.1 使用模块

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

# mysingleton.py
class Singleton(object):
    def foo(self):
        pass
    
singleton = Singleton()

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

from a import singleton
4.2 使用装饰器
def Singleton(cls):
    _instance = {}

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

    return _singleton


@Singleton
class Test(object):
    t = 1

    def __init__(self, x=0):
        self.x = x


t1 = Test(2)
t2 = Test(3)
4.3 懒汉式 非线程安全的单例模式

**描述:**这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。
这种方式 lazy loading 很明显,不要求线程安全,在多线程不能正常工作。

class Singleton(object):
	__instance = None

	def __new__(cls, name, age):
		# 如果类属性__instance的值为None,那么就创建一个对象
		if not cls.__instance:
			cls.__instance = object.__new__(cls)
		# 如果已经有实例存在,直接返回
		return cls.__instance

a = Singleton("Zhangsan", 18)
b = Singleton("lisi", 20)

print(id(a))
print(id(b))

a.age = 30   # 给a指向的对象添加一个属性
print(b.age)  # 获取b指向的对象的age属性
4.4 懒汉式 线程安全的单例

**描述:**这种方式具备很好的 lazy loading,能够在多线程中很好的工作,但是,效率很低,99% 情况下不需要同步。
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。

import threading

"""
线程安全的单利模式

紧跟with后面的语句被求值后,返回对象的 __enter__() 方法被调用,这个方法的返回值将被赋值给as后面的变量。
当with后面的代码块全部被执行完之后,将调用前面返回对象的 __exit__()方法
"""
def synchronized(func):
    func.__lock__ = threading.Lock()

    def lock_func(*args, **kwargs):
        with func.__lock__:
            return func(*args, **kwargs)

    return lock_func

class Singleton(object):
    instance = None

    @synchronized
    def __new__(cls):
        # 关键在于这,每一次实例化的时候,我们都只会返回这同一个instance对象
        if not cls.instance:
            cls.instance = super(Singleton, cls).__new__(cls)
        return cls.instance

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值