Python 单例模式:5种实现方式

目录

  • 单例模式
    • 模块实现单例
    • 类装饰器实现
    • 基于类绑定方法
    • 使用new方法实现
    • 元类实现单例模式


单例模式

单例模式(Singleton mode):一种常见的软件设计模式,该设计模式主要目的就是确保某一个类中只能有一个实例存在。

如:产生了很多实例,而它们做的事情只是执行发一个邮件的功能。这会特别浪费内存资源,因为频繁创建和销毁实例。

优点:

1、由于单例模式在内存中只有一个实例,减少内存开支,特别是一个对象需要频繁地创建销毁时,而且创建或销毁时性能又无法优化,单例模式就非常明显了
2、单例模式可以避免对资源的多重占用。单例一次只接受一个使用者,如果还要访问使用,需要等待上一个使用完。

缺点:

1、不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2、单例模式没有抽象层,扩展很困难,若要扩展,除了修改代码别无选择


模块实现单例

在了解过模块后可以得知,第一次访问模块以后,会生成将一个.pyc文件,将保存第一次访问的状态。而此后的每次访问模块都是直接加载那个.pyc文件。从这一点就可以得知,Python模块是一个纯天然的单例模式。

开始实例:

# test.py
class People:
    pass
        
people = People()

# run.py
from test import people

p1 = people

p2 = people

print(p1 is p2) # is比较的是两个对象的内存地址,如果相同,则使用的是同一个实例

执行结果

True

类装饰器实现

def Signleton(cls):
    instance = False  # 做一个标识,用于第一次返回实例

    def inner(*args, **kwargs):
        nonlocal instance

        if not instance:  # 如果为假,则创建一个实例
            instance = cls(*args, **kwargs)  # 调用People类,产生一个实例

        instance.__init__(*args, **kwargs)  # 让这个实例的属性可变化
        return instance  # 返回这个实例

    # 当instance拿到实例以后,if将不再执行,所以每次返回的都是第一次产生的实例

    return inner


@Signleton
class People:  # People = Signleton(People) = inner
    def __init__(self, name):
        self.name = name

    def send_email(self):
        print(f'{self.name}发送了邮箱~')


p1 = People('jack')  # 执行了inner方法
p1.send_email()

p2 = People('tom')
p2.send_email()

p1.send_email() # 再次执行p1,查看是否保存了状态

print(p1 is p2)  # is比较的是两个对象的内存地址,如果相同,则使用的是同一个实例

执行结果

'jack发送了邮箱~'
'tom发送了邮箱~'
'tom发送了邮箱~'
True

可以看到,因为使用的是同一个实例,第二次使用时更改了这个实例的属性。所以再次打印内容也就是最后一次更改的结果


基于类绑定方法

class People:
    instance = False # 使用类属性,做一个标识

    def __init__(self,name):
        self.name = name

    @classmethod # 类绑定方法
    def get_signleton_obj(cls,*args,**kwargs):
        if not cls.instance: # 如果instance为假,则创建一个实例
            cls.instance = cls(*args,**kwargs)
            # 调用People类,产生一个实例,并赋给instance属性
		
        cls.instance.__init__(*args,**kwargs) # 让调用者可以更改实例的属性
        return cls.instance

    def send_email(self):
        print(f'{self.name}发送了邮箱~')

p1 = People.get_signleton_obj('jack')
p1.send_email()

p2 = People.get_signleton_obj('tom')
p2.send_email()

print(p1 is p2) # is比较的是两个对象的内存地址,如果相同,则使用的是同一个实例
'jack发送了邮箱~'
'tom发送了邮箱~'
True

使用new方法实现

class People:
    instance = None # 类属性做一个标识
    
    def __new__(cls, *args, **kwargs):
        if not cls.instance: # 如果instance为假,则创建一个实例
            cls.instance = super().__new__(cls)

        return cls.instance
	
	# 我们不需要手动来调用,因为执行完new以后,会调用init来初始化属性
    def __init__(self,name):
        self.name = name

    def send_email(self):
        print(f'{self.name}发送了邮箱~')

p1 = People('jack')
p1.send_email()

p2 = People('tom')
p2.send_email()

print(p1 is p2)

执行结果

'jack发送了邮箱~'
'tom发送了邮箱~'
True

元类实现单例模式

class MyType(type):
    instance = False  # 元类的属性

    def __call__(self, *args, **kwargs):
        # 先去自身及父类里面找instance,没有找到该属性则找到元类里面了
        if not self.instance:
            # 给自身类增加一个属性,该属性值就是一个实例。
            self.instance = super().__call__(*args, **kwargs)

        # 每次都可以为这个实例设置新的属性
        self.instance.__init__(*args, **kwargs)
        
        return self.instance # 返回这个实例

		# 因为我们类的属性名设置了instance,所以下一次就会在自身找到,判断为True取反,不执行if,所以每次拿到的都是第一次的实例


class People(metaclass=MyType):

    def __init__(self,name):
        self.name = name

    def send_email(self):
        print(f'{self.name}发送了邮箱~')


p = People('jack')
p.send_email()

p1 = People('tom')
p1.send_email()

print(id(p), id(p1))  # 查看两个对象的内存地址,如果相同则是同一个对象

以上就是笔者所能想到使用Python实现单例模式的5种方式


技术小白记录学习过程,有错误或不解的地方请指出,如果这篇文章对你有所帮助请点赞 收藏+关注 子夜期待您的关注,谢谢支持!

你可能感兴趣的:(Python进阶,python,设计模式,单例模式)