起步
单例模式是一种常用的软件设计模式,用来确保一个类只会有一个实例存在。
以下是 Python 中实现单例模式的多种方法,环境基于 Python3.6.6。
\__new\__函数
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)
每次实例化一个对象时,都会先调用 __new__()
创建一个对象,再调用 __init__()
函数初始化数据。因而,在 new 函数中判断 Singleton类 是否已经实例化过,如果不是,调用父类的 new 函数创建实例;否则返回之前创建的实例。
_instance 作为类属性,保证了所有对象的 _instance 都是同一个。
元类 metaclass
class SingletonMetaClass(type):
_instance = None
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super().__call__(*args, **kwargs)
return self._instance
class Singleton(metaclass=SingletonMetaClass):
pass
# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)
当一个类设置了元类以后,创建对象是通过调用元类中的 __call__()
函数实现。这一点你可以通过对以下代码做断点运行来理解:
class SigletonMetaClass(type):
_instance = None
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs) # 断点1
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super().__call__(*args, **kwargs)
return self._instance # 断点2
class Singleton(metaclass=SigletonMetaClass):
def __new__(cls, *args, **kwargs):
return super().__new__(cls) # 断点3
因此,用元类实现单例时仍需按照三步骤:1. 拦截 2. 判断是否已经创建过对象 3. 返回对象。与上个方法相比,区别在于拦截的地点不同。
装饰器
用装饰器实现单例,思路与其他一致,改变的同样是拦截地点与记录位置。
函数装饰器
def SingletonDecorator(cls):
_instance = None
def get_instance(*args, **kwargs):
nonlocal _instance
if _instance is None:
_instance = cls(*args, **kwargs)
return _instance
return get_instance
@SingletonDecorator
class Singleton(object):
pass
# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)
类装饰器
class SingletonDecorator(object):
_instance = None
def __init__(self, cls):
self._cls = cls
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = self._cls(*args, **kwargs)
return self._instance
@SingletonDecorator
class Singleton(object):
pass
# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)
方法
静态方法 staticmethod
class Singleton(object):
_instance = None
@staticmethod
def get_instance():
cls = __class__
if cls._instance is None:
cls._instance = super(cls, cls).__new__(cls)
return cls._instance
# 示例:
a = Singleton.get_instance()
b = Singleton.get_instance()
# id(a) == id(b)
在静态函数中,既不会传入 cls 也不会有 self。为了在静态函数中使用 cls 的同时,避免硬编码,可使用内置变量 __class__
。在一个类的作用域中,__class__
等于类对象,即:__class__ == Singleton
。
类方法 classsmethod
class Singleton(object):
_instance = None
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
# 示例:
a = Singleton.get_instance()
b = Singleton.get_instance()
# id(a) == id(b)
与静态函数类似。
名字覆盖
class Singleton(object):
def __call__(self):
return self
Singleton = Singleton()
# 示例:
a = Singleton()
b = Singleton()
# id(a) == id(b)
用实例名覆盖类名后,执行 Singleton()
就是在调用 __call__()
函数,总是返回自身。
属性共享
class Singleton(object):
_state = {}
def __new__(cls, *args, **kwargs):
obj = super().__new__(cls)
obj.__dict__ = cls._state
return obj
def __init__(self, name):
self.name = name
# 示例:
a = Singleton()
b = Singleton()
# id(a) != id(b)
实例对象的私有属性存放在 __dict__
中。因此,将所有对象指向同一个属性 Singleton._state,即便它们的 id值 不同,由于共享属性仍实现了单例效果。
还不快抢沙发