跳到主要内容

05、Python - 单例模式

基本介绍

单例模式(Singleton Pattern)即一个类只能拥有一个实例对象,实例化多次的结果都会指向同一个对象。

特点:全局唯一,允许更改

该模式属于创建型模式,同时也是创建型模式中应用最为广泛的一种。

案例图示

从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了,只有在配置不同的情况下才会生成新的实例。

文件内容如下:

# settings.py文件内容如下

HOST = "localhost"
PORT = 3306

案例图示:

 

优缺点

优点:

  • 避免对资源的多重占用,如写入文件操作
  • 节省内存
  • 防止命名空间被污染

缺点:

  • 没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化

代码实现

Python实现单例模式手段较多,这里例举5种比较常见的。

继承实现

基础代码如下:

class Singleton:
    def __new__(cls, *args, **kwargs) -> object:
        """
        cls : class Category
        """
        if not hasattr(cls, "ins"):
            singletonInsObject = super(__class__, cls).__new__(cls, *args, **kwargs)
            setattr(cls, "ins", singletonInsObject)
        return getattr(cls, "ins")
class Category(Singleton):
    pass
if __name__ == "__main__":
    ins = Category()
    print(id(ins))
    ins = Category()
    print(id(ins))

元类实现

基础代码如下:

class MetaClass(type):
    def __call__(self, *args, **kwargs):
        """
        self : class Category
        """
        if not hasattr(self, "ins"):
            singletonInsObject = super(__class__, self).__call__(*args, **kwargs)
            setattr(self, "ins", singletonInsObject)
        return getattr(self, "ins")
class Category(object, metaclass=MetaClass):
    pass
if __name__ == "__main__":
    ins = Category()
    print(id(ins))
    ins = Category()
    print(id(ins))

装饰器实现

基础代码如下:

def warpper(clsObject):
    def inner(*args, **kwargs):
        if not hasattr(clsObject, "ins"):
            singletonInsObject = clsObject(*args, **kwargs)
            setattr(clsObject, "ins", singletonInsObject)
        return getattr(clsObject, "ins")
    return inner
@warpper
class Category:
    pass
if __name__ == "__main__":
    ins = Category()
    print(id(ins))
    ins = Category()
    print(id(ins))

模块实现

基础代码如下:

- foo.py --> ins = Category()
- bar.py --> from foo import ins

@classmethod实现

基础代码如下:

class Category:

    @classmethod
    def getSingletonInstanceObject(cls, *args, **kwargs):
        if not hasattr(cls, "ins"):
            singletonInsObject = cls(*args, **kwargs)
            setattr(cls, "ins", singletonInsObject)
        return getattr(cls, "ins")
if __name__ == "__main__":
    ins = Category.getSingletonInstanceObject()
    print(id(ins))
    ins = Category.getSingletonInstanceObject()
    print(id(ins))

案例实现

案例实现采用元类实现、装饰器实现以及@classmethod实现.

1)元类实现:

import settings
class MetaClass(type):
    """
    self : class MySQL
    """
    def __call__(self, *args, **kwargs):

        常规实例化
        if args or kwargs:
            insObject = object.__new__(self)
            self.__init__(insObject, *args, **kwargs)
            return insObject

        采用默认配置实例化,生成单例对象
        if not hasattr(self, "ins"):
            singletonInsObject = object.__new__(self)
            self.__init__(singletonInsObject, settings.HOST, settings.PORT)
            setattr(self, "ins", singletonInsObject)
        return getattr(self, "ins")
class MySQL(object, metaclass=MetaClass):
    def __init__(self, host, port) -> None:
        self.host = host
        self.port = port
if __name__ == "__main__":
    ins1 = MySQL("192.168.0.1", 3306)
    ins2 = MySQL("192.168.0.1", 3307)
    print(ins1 is ins2)

    ins3 = MySQL()
    ins4 = MySQL()
    print(ins3 is ins4)

# False
# True

2)装饰器实现:

import settings
def warpper(cls):
    singletonInsObject = cls(settings.HOST, settings.PORT)

    def inner(*args, **kwargs):
        常规实例化
        if args or kwargs:
            insObject = cls(*args, **kwargs)
            return insObject
            
        采用默认配置实例化,生成单例对象
        return singletonInsObject
    return inner
@warpper
class MySQL:

    def __init__(self, host, port) -> None:
        self.host = host
        self.port = port
if __name__ == "__main__":
    ins1 = MySQL("192.168.0.1", 3306)
    ins2 = MySQL("192.168.0.1", 3307)
    print(ins1 is ins2)

    ins3 = MySQL()
    ins4 = MySQL()
    print(ins3 is ins4)

# False
# True

3)@classmethod实现:

import settings
class MySQL:
    singletonInsObject = None

    常规实例化
    def __init__(self, host, port) -> None:
        self.host = host
        self.port = port

    采用默认配置实例化,生成单例对象
    @classmethod
    def getSingletonInstanceObject(cls):
        if not cls.singletonInsObject:
            cls.singletonInsObject = cls(settings.HOST, settings.PORT)
        return cls.singletonInsObject

if __name__ == "__main__":
    ins1 = MySQL("192.168.0.1", 3306)
    ins2 = MySQL("192.168.0.1", 3307)
    print(ins1 is ins2)

    ins3 = MySQL.getSingletonInstanceObject()
    ins4 = MySQL.getSingletonInstanceObject()
    print(ins3 is ins4)

# False
# True