【Python编程】在 Python 中如何实现单例模式

本文详细介绍了Python中实现单例模式的四种方法:直接导入、利用`__new__`方法、装饰器以及元类,并讨论了单例模式的优缺点及其应用场景。此外,还提到了在多线程环境下实现线程安全单例的注意事项。单例模式常用于数据库连接池、配置对象和日志操作等共享资源的场景。
摘要由CSDN通过智能技术生成

在 Python 中如何实现单例模式

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

方法一

实例化一个的对象,要用时直接 import 导入这个对象,而不是再实例化一个,这样就做到了单例模式了,每次都用的是同一个对象。

方法二

我们知道,当我们实例化一个对象时,是先执行了类的 __new__ 方法(我们没写时,默认调用 object.new),实例化对象;然后再执行类的 __init__ 方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式。

class Count(object):
    def __init__(self):
        print('将对象初始化')

    def __new__(cls, *args, **kwargs):
        print('new一个对象')
        cls.obj = object.__new__(cls)
        return cls.obj

Count()

在这里插入图片描述

从以上结果中我们看到,使用类创建对象时,先自动执行了 new 方法创建对象,然后返回给 init 进行初始化操作。

接下来进行改写。

class Count(object):
    # 设置一个类属性来判断这个类是否实例化过对象
    instance = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if cls.instance == None:
            cls.instance = object.__new__(cls)
            return cls.instance
        else:
            return cls.instance

t1 = Count()
t2 = Count()
t3 = Count()
print(t1)
print(t2)
print(t3)

在这里插入图片描述
以上,通过 new 方法内判断是否创建过对象,来实现单例模式,我们实例化三个对象 t1t2t3 三个对象内存地址都是一样的,这就说明他们是同一个对象。

方法三

通过装饰器实现单例模式,任意一个类使用了该装饰器就会变成一个单例模式的类。

装饰器是 Python 中非常有特色的语法,用一个函数去装饰另一个函数或类,为其添加额外的能力。通常通过装饰来实现的功能都属横切关注功能,也就是跟正常的业务逻辑没有必然联系,可以动态添加或移除的功能。装饰器可以为代码提供 缓存、代理、上下文环境 等服务,它是对设计模式中代理模式的践行。

装饰器的作用和功能:

  • 引入日志
  • 函数执行时间统计
  • 执行函数前预备处理
  • 执行函数后的清理功能
  • 权限校验等场景
  • 缓存

总的来说,装饰器 wrapper 是一个函数的工厂,他接收一个函数参数,返回的也是一个函数。一般其作用是将原有函数添加上新的一些功能,然后将新的功能更强的函数返回。

def Single_Class(cls):
    instance = {}

    def single(*args, **kwargs):
        if cls not in instance:
            # 如果类没有创建过,那就new一个新的,并保存在字典里
            instance[cls] = cls(*args, **kwargs)
            return instance[cls]
        else:
            return instance[cls]
    return single

@Single_Class
class A():
    pass

t1 = A()
t2 = A()
print(t1)
print(t2)

在这里插入图片描述

以上,我们写了一个装饰器,该装饰器将所有的对象都存储到字典里,如果之前创建过的对象就直接返回,如果没有创建过的对象那就 new 一个,同一个类我们实例化了 t1t2,但是他们的内存地址都是同一个,说明我们已经实现了单例模式的类装饰器。

接下来对上述代码做一些小的改动。

from functools import wraps

def Single_Class(cls):
    instance = {}

    @wraps(cls) # wraps是在装饰器中使用,保留原来函数的属性
    def single(*args, **kwargs):
        if cls not in instance:
            # 如果类没有创建过,那就new一个新的,并保存在字典里
            instance[cls] = cls(*args, **kwargs)
            return instance[cls]
        else:
            return instance[cls]
    return single

@Single_Class
class A():
    pass

在写装饰器的时候,带装饰功能的函数(上面代码中的 single 函数)通常都会用functools模块中的 wraps 再加以装饰,这个装饰器最重要的作用是给被装饰的类或函数动态添加一个 __wrapped__ 属性,这个属性会将被装饰之前的类或函数保留下来,这样在我们不需要装饰功能的时候,可以通过它来取消装饰器,例如可以使用 A = A.__wrapped__ 来取消对 A 类做的单例处理。

需要提醒大家的是:上面的单例并不是线程安全的,如果要做到线程安全,需要对创建对象的代码进行加锁的处理。在 Python 中可以使用 threading 模块的 RLock 对象来提供锁,可以使用锁对象的 acquirerelease 方法来实现加锁和解锁的操作。当然,更为简便的做法是使用锁对象的 with 上下文语法来进行隐式的加锁和解锁操作。

方法四

使用元类实现单例模式。这个方法和方法二其实思想上是一致的。

class SingletonMeta(type):
    """自定义单例元类"""

    def __init__(cls, *args, **kwargs):
        cls.__instance = None
        super().__init__(*args, **kwargs)  # 继承父类中的__init__

    def __call__(cls, *args, **kwargs):
        if cls.__instance is None:
            cls.__instance = super().__call__(*args, **kwargs)
        return cls.__instance

class President(metaclass=SingletonMeta):
    pass

t1 = President()
t2 = President()
print(t1)
print(t2)

在这里插入图片描述

Python 是面向对象的编程语言,在面向对象的世界中,一切皆为对象。对象是通过类来创建的,而类本身也是对象,类这样的对象是通过元类来创建的。我们在定义类时,如果没有给一个类指定父类,那么默认的父类是 object;如果没有给一个类指定元类,那么默认的元类是 type。通过自定义的元类,我们可以改变一个类默认的行为,就如同上面的代码中,我们通过元类的 __call__ 魔术方法,改变了 President 类的构造器那样。

单例模式的优点:

  • 节约内存,因为不管实例化多少个 对象都实际指向一个内存地址;
  • 多个地方创建的实例他们的属性互动,比如:我们只给实例化对象 t1 添加了一个 name 属性,而 t2 和 t3 都拥有了这个属性,这就给我们节约了很多的时间。

关于单例模式,还有很多其他的应用场景。通常一个对象的状态是被其他对象共享的,就可以将其设计为单例。例如:

  • 项目中使用的数据库连接池对象和配置对象通常都是单例,这样才能保证所有地方获取到的数据库连接和配置信息是完全一致的。
  • 而且由于对象只有唯一的实例,因此从根本上避免了重复创建对象造成的时间和空间上的开销,也避免了对资源的多重占用。
  • 再举个例子,项目中的日志操作通常也会使用单例模式,这是因为共享的日志文件一直处于打开状态,只能有一个实例去操作它,否则在写入日志的时候会产生混乱。

参考资料

【1】【建议收藏】50 道硬核的 Python 面试题!
【2】python 实现单例模式的几种方法实例详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

G皮T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值