Python小白学习教程从入门到入坑------第二十六课 单例模式(语法进阶)

在这个节课的开始,我们先回顾一下面向对象课程中学的构造函数__init__()

目录

[一、init() 和 new()](#一、init() 和 new())

[1.1 init()](#1.1 init())

[1.2 new()](#1.2 new())

二、单例模式

[2.1 特点](#2.1 特点)

[2.2 通过@classmethod实现单例模式](#2.2 通过@classmethod实现单例模式)

[2.3 通过装饰器实现单例模式](#2.3 通过装饰器实现单例模式)

[2.3 通过重写__new__() 实现单例模式](#2.3 通过重写__new__() 实现单例模式)

[2.4 通过导入模块实现单例模式](#2.4 通过导入模块实现单例模式)

三、单例模式的应用场景




一、init() 和 new()

1.1 init()

作用:初始化对象

eg:

python 复制代码
class Test(object):
    def __init__(self):
        print("这是__init__()")
te = Test()
# 输出结果:这是__init__()

1.2 new()

new(): object 基类提供的内置的静态方法

作用:

1、在内存中为对象分配空间

2、返回对象的引用

eg:

python 复制代码
class Test(object):
    def __init__(self):
        print("这是__init__()")
    def __new__(cls, *args, **kwargs):     # cls代表类本身
        print("我是__new__()")
te = Test()
# 输出结果:我是__new__()

我们会发现,这里面的__init__方法没有输出 ,这是为什么呢?

其实是因为我们在__new__方法中将__new__方法改写了,改变了python中默认__new__的功能,改成了print("我是__new__()"),所以__init__方法没有输出

我们接下来试着打印一下te和cls

eg:没有__new__() 方法时

python 复制代码
class Test(object):
    def __init__(self):
        print("这是__init__()")
te = Test()
print(te)
# 输出结果:
# 这是__init__()
# <__main__.Test object at 0x000001BF8ABFE148>

加入__new__() 方法时:

python 复制代码
class Test(object):
    def __init__(self):
        print("这是__init__()")
    def __new__(cls, *args, **kwargs):
        print("我是__new__()")
        print(cls)
te = Test()
print(te)
# 输出结果:
# 我是__new__()
# <class '__main__.Test'>
# None

发现此时te输出为None,就是因为__new__的功能被覆盖改写了

那么我们如果想修改原有的代码,却还想继承原有代码的功能,那么我们该怎么做呢?

我们需要用到之前学习的扩展

eg:

python 复制代码
class Test(object):
    def __init__(self):
        print("这是__init__()")
    def __new__(cls, *args, **kwargs):
        print("我是__new__()")
        print(cls)
        # 对父类方法进行扩展   推荐使用super().方法名()
        res = super().__new__(cls)
        # 方法重写,res里面保存的是实例对象的引用,__new__()是静态方法,形参里面有cls,实参就必须传cls
        return res
        # 注意:重写__new__() 一定要return super().__new__(cls),否则python解释器得不到分配空间的对象引用,就不会调用__init__()
te = Test()
print("te:",te)
# 输出结果:
# 我是__new__()
# <class '__main__.Test'>
# 这是__init__()
# te: <__main__.Test object at 0x0000021EBEBEE548>

执行步骤:

一个对象的实例化过程:首先执行__new__(),如果没有写__new__(),默认调用object 里面的__new__(),返回一个实例对象,然后再去调用__init__(),对对象进行初始化

eg:

python 复制代码
class Person(object):
    def __new__(cls, *args, **kwargs):
        print("这是new方法")
        print("返回值:",super().__new__(cls))
        return super().__new__(cls)
    def __init__(self,name):
        self.name = name   # 实例属性
        print("名字是:",self.name)
pe = Person('junjun')
print(pe)
pe2 = Person('susu')
print(pe2)
# 输出结果:
# 这是new方法
# 返回值: <__main__.Person object at 0x000002564FA1E5C8>
# 名字是: junjun
# <__main__.Person object at 0x000002564FA1E5C8>
# 这是new方法
# 返回值: <__main__.Person object at 0x000002564FA1E608>
# 名字是: susu
# <__main__.Person object at 0x000002564FA1E608>

总结:init() 和 new()

1、new() 是创建对象,init() 是初始化对象

2、new() 是返回对象引用,init() 定义实例属性

3、new() 是类级别的方法,init() 是实例级别的方法

二、单例模式

2.1 特点

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例

当你希望在整个系统中,某个类只能出现一个实例时,单例模式就能排上用场(可以简单理解成一个特殊的类,这个类只存在一个对象)

优点:可以节省内存空间,减少了不必要的资源浪费

弊端:多线程访问的时候容易引发线程安全问题

在 Python 中,有多种方式实现单例模式,以下是一些常见的方法:

1、通过@classmethod

2、通过装饰器实现

3、通过重写__new__() 实现(重点)

4、通过导入模块实现

注意:单例模式简单来说就是每一次实例化所创建的对象都是同一个,即内存地址相同

2.2 通过@classmethod实现单例模式

这种方法利用类方法 @classmethod 和一个类变量来追踪单例实例

eg:

python 复制代码
class Singleton:  
    _instance = None  
  
    @classmethod  
    def get_instance(cls, *args, **kwargs):  
        if cls._instance is None:  
            cls._instance = cls(*args, **kwargs)  
        return cls._instance  
  
    def __init__(self, value=None):  
        if not hasattr(self, 'initialized'):  # 防止重复初始化  
            self.value = value  
            self.initialized = True  
  
# 测试  
if __name__ == "__main__":  
    s1 = Singleton(10)  
    s2 = Singleton.get_instance(20)  # 这里传递的 20 将被忽略,因为实例已经存在  
    print(s1.value)  # 输出: 10  
    print(s2.value)  # 输出: 10,而不是 20  
    print(s1 is s2)  # 输出: True

注意:上面的实现中,init 方法被修改以防止重复初始化。这是因为在第一次创建实例后,后续通过 get_instance 获取实例时不会再次调用 init

2.3 通过装饰器实现单例模式

装饰器可以用来将任何类转换为单例

python 复制代码
def singleton(cls):  
    instances = {}  
    def get_instance(*args, **kwargs):  
        if cls not in instances:  
            instances[cls] = cls(*args, **kwargs)  
        return instances[cls]  
    return get_instance  
  
@singleton  
class Singleton:  
    def __init__(self, value):  
        self.value = value  
  
# 测试  
if __name__ == "__main__":  
    s1 = Singleton(10)  
    s2 = Singleton(20)  # 这里传递的 20 将被忽略,因为实例已经存在(通过装饰器控制)  
    print(s1.value)  # 输出: 10  
    print(s2.value)  # 输出: 10  
    print(s1 is s2)  # 输出: True

2.3 通过重写__new__() 实现单例模式

这是最常见和推荐的方法之一,因为它在对象创建的最早阶段就进行控制

设计流程:

1、定义一个类属性,初始值为None,用来记录单例对象的引用

2、重写__new__() 方法

3、进行判断,如果类属性是None,把__new__() 返回的对象引用保存进去

4、返回类属性中记录的对象引用

python 复制代码
class Singleton:  
    _instance = None   # 类属性
  
    def __new__(cls, *args, **kwargs):  
        if cls._instance is None:  # 判断类属性是否为空
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)  
        return cls._instance  
  
    def __init__(self, value=None):  
        if not hasattr(self, 'initialized'):  # 防止重复初始化(同样的问题)  
            self.value = value  
            self.initialized = True  
  
# 测试  
if __name__ == "__main__":  
    s1 = Singleton(10)  
    s2 = Singleton(20)  # 这里传递的 20 将被忽略  
    print(s1.value)  # 输出: 10  
    print(s2.value)  # 输出: 10  
    print(s1 is s2)  # 输出: True

2.4 通过导入模块实现单例模式

Python 模块在第一次导入时会被初始化,并且只会被初始化一次。因此,可以利用这一特性实现单例。

首先,创建一个模块 singleton_module.py:

python 复制代码
# singleton_module.py  
class Singleton:  
    def __init__(self, value):  
        self.value = value  
  
singleton_instance = Singleton(10)  # 创建单例实例

然后,在其他文件中使用:

python 复制代码
# main.py  
import singleton_module  
  
# 测试  
if __name__ == "__main__":  
    s1 = singleton_module.singleton_instance  
    # 尝试重新赋值不会改变单例实例  
    # singleton_module.singleton_instance = Singleton(20)  # 这行不会影响已经存在的实例  
    # 但可以通过实例的属性进行修改(如果允许的话)  
    # s1.value = 20  # 这会改变实例的 value 属性  
      
    s2 = singleton_module.singleton_instance  
    print(s1.value)  # 输出: 10  
    print(s2 is s1)  # 输出: True

注意:在模块级单例中,如果直接修改 singleton_module.singleton_instance 指向另一个对象,那么会破坏单例模式。但是,如果通过实例的属性进行修改(如 s1.value = 20),则不会破坏单例模式,只是改变了实例的状态

三、单例模式的应用场景

1.回收站对象

2.音乐播放器,一个音乐播放软件负责音乐播放的对象只有一个

3.开发游戏软件场景管理器

4.数据库配置、数据库连接池的设计

今天的分享就到这里了,希望本文能够对大家有些许的帮助~

相关推荐
databook10 小时前
Manim实现闪光轨迹特效
后端·python·动效
Juchecar11 小时前
解惑:NumPy 中 ndarray.ndim 到底是什么?
python
用户83562907805112 小时前
Python 删除 Excel 工作表中的空白行列
后端·python
Json_12 小时前
使用python-fastApi框架开发一个学校宿舍管理系统-前后端分离项目
后端·python·fastapi
数据智能老司机18 小时前
精通 Python 设计模式——分布式系统模式
python·设计模式·架构
数据智能老司机19 小时前
精通 Python 设计模式——并发与异步模式
python·设计模式·编程语言
数据智能老司机19 小时前
精通 Python 设计模式——测试模式
python·设计模式·架构
数据智能老司机19 小时前
精通 Python 设计模式——性能模式
python·设计模式·架构
c8i20 小时前
drf初步梳理
python·django
每日AI新事件20 小时前
python的异步函数
python