Python小白学习教程从入门到入坑------第二十六课 单例模式(语法进阶)
在这个节课的开始,我们先回顾一下面向对象课程中学的构造函数__init__()
一、__init__() 和 __new__()
1.1 __init__()
作用:初始化对象
eg:
class Test(object):
def __init__(self):
print("这是__init__()")
te = Test()
# 输出结果:这是__init__()
1.2 __new__()
__new__(): object 基类提供的内置的静态方法
作用:
1、在内存中为对象分配空间
2、返回对象的引用
eg:
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__() 方法时
class Test(object):
def __init__(self):
print("这是__init__()")
te = Test()
print(te)
# 输出结果:
# 这是__init__()
# <__main__.Test object at 0x000001BF8ABFE148>
加入__new__() 方法时:
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:
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:
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:
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 通过装饰器实现单例模式
装饰器可以用来将任何类转换为单例
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__() 实现单例模式
这是最常见和推荐的方法之一,因为它在对象创建的最早阶段就进行控制
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:
# singleton_module.py
class Singleton:
def __init__(self, value):
self.value = value
singleton_instance = Singleton(10) # 创建单例实例
然后,在其他文件中使用:
# 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),则不会破坏单例模式,只是改变了实例的状态
今天的分享就到这里了,希望本文能够对大家有些许的帮助~