python单例实现及注意事项

简介:python单例,不同方式的注意事项

什么是单例

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。

Python的单例实现有三种方式,最不同的就是通过 __new__ 魔法方法来实现,这种方式类似与PHP中的单例的实现。除此之外还可以通过装饰器以及元类实现单例

1. 通过___new___实现单例

class Instance():
    _instance = None
    
    def __init__(self):
        print('初始化')
        
    def __new__(cls):
        if not cls._instance:  
            cls._instance = super().__new__(cls)  
        return cls._instance

a = Instance()
c = Instance()
print(id(a))
print(id(c))

代码执行打印结果:

初始化
初始化
2889223725568
2889223725568

从打印的结果可以看出,这种方式实现的单例会调用__init__魔术方法多次,但是最总实例化的对象的指针id是同一个,表明他们指向同一个对象。

2. 通过装饰器实现单例(推荐)

既然是通过装饰器实现单例,那我们就必须先定义一个装饰器函数:

def singleton(cls):
    instances = {}
    def getinstance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]
    return getinstance

这个装饰器,我们可以把它封装到某个公共文件中,以提高单例装饰器的复用性。

接着就可以在任意类上面使用装饰器来表明这是一个单例即可,具体代码方式如下:

@singleton
class MyClass:
    def __init__(self):
        print('初始化')

a = MyClass()
b = MyClass()
print(id(a))
print(id(b))

上述代码执行打印结果如下:

初始化
1203478770160
1203478770160

从上面的结果就可以看出,实例化的对象a及b的内存地址id是相同的,表示他们是同一个对象。此外,这种方式实现的单例只会调用一次 __init__ 魔术方法,这是与第一种方式实现的单例完全不同。

装饰器实现单例时不能使用类变量,否则会直接报错。例如下方代码:

@singleton
class MyClass:
    # 类属性
    frame_processor = None

    def __init__(self):
        print('初始化')

    def get_frame_processor(self):
        return MyClass.frame_processor 

a = MyClass()
a.get_frame_processor()

使用装饰器实现单例后,get_frame_processor对象方法内无法通过MyClass获取类属性,否则会报错:'function' object has no attribute 'frame_processor'

3. 通过元类实现单例

元类方式结合了第一种和装饰器的两种方式。只不过装饰器变成了一个基类,具体代码如下:

class Singleton(type):  
    _instances = {}  
    def __call__(cls, *args, **kwargs):  
        if cls not in cls._instances:  
            cls._instances[cls] = super().__call__(*args, **kwargs)  
        return cls._instances[cls]  

关于这个基类,我们可以和装饰器一样封装到一个公共的文件中。

子类如果想要实现单例,就必须继承 Singleton 这个基类,并且是以元类的方式继承。具体实现方式如下:

class MyClass(metaclass=Singleton):  
    def __init__(self):
        print('初始化')

a = MyClass()
b = MyClass()
print(id(a))
print(id(b))

上述代码执行的结果如下:

初始化
2270477843856
2270477843856

执行结果和装饰器的一致,没有什么区别。但是日常工作我个人建议大家还是使用装饰器来实现单例,毕竟自己实现的类可能需要继承其他类,如果以元类的方式实现,就会显得代码比较臃肿,我个人不太喜欢。

有遗漏或者不对的可以在我的公众号留言哦

编程经验共享公众号二维码

编程经验共享公众号二维码
更多内容关注公众号
Copyright © 2021 编程经验共享 赣ICP备2021010401号-1