我有多个类需要成为单例(我的用例是一个记录器,但这不重要)。我不希望在几个类中添加额外的内容,而是可以简单地继承或装饰。
最佳方法:
方法1:装饰器
def singleton(class_):
instances = {}
def getinstance(*args, **kwargs):
if class_ not in instances:
instances[class_] = class_(*args, **kwargs)
return instances[class_]
return getinstance
@singleton
class MyClass(BaseClass):
pass
优点
- 装饰器的特性通常比多重继承更加直观。
缺点
While objects created using
MyClass()
would be true singleton objects,MyClass
itself is a function, not a class, so you cannot call class methods from it. Also forx = MyClass(); y = MyClass(); t = type(n)();
那么x == y
,但是x != t && y != t
方法二:基类
class Singleton(object):
_instance = None
def __new__(class_, *args, **kwargs):
if not isinstance(class_._instance, class_):
class_._instance = object.__new__(class_, *args, **kwargs)
return class_._instance
class MyClass(Singleton, BaseClass):
pass
优点
- 它是一个真正的类
缺点
- 多重继承 - 呃!从第二个基类继承时可能会覆盖
__new__
?这需要比必要更多的思考。
方法三:元类
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
#Python2
class MyClass(BaseClass):
__metaclass__ = Singleton
#Python3
class MyClass(BaseClass, metaclass=Singleton):
pass
优点
- 它是一个真正的类
- 自动覆盖继承
- 使用
__metaclass__
实现其正确的目的(并让我意识到了这一点)
缺点
- 有吗?
方法四:返回同名类的装饰器
def singleton(class_):
class class_w(class_):
_instance = None
def __new__(class_, *args, **kwargs):
if class_w._instance is None:
class_w._instance = super(class_w,
class_).__new__(class_,
*args,
**kwargs)
class_w._instance._sealed = False
return class_w._instance
def __init__(self, *args, **kwargs):
if self._sealed:
return
super(class_w, self).__init__(*args, **kwargs)
self._sealed = True
class_w.__name__ = class_.__name__
return class_w
@singleton
class MyClass(BaseClass):
pass
优点
- 它是一个真正的类
- 自动地覆盖继承
缺点
- 创建每个新类是否有开销?在这里,我们为每个要使其成为单例的类创建了两个类。虽然在我的情况下这很好,但我担心这可能不能扩展。当然,是否应该轻松扩展这种模式存在争议...
_sealed
属性的意义是什么- 无法使用
super()
调用基类上同名的方法,因为它们会递归。这意味着您无法自定义__new__
并且无法子类化需要您调用__init__
的类。
方法五:一个模块
一个名为singleton.py
的模块文件。
优点
- 简单胜于复杂。
缺点
- 不是延迟实例化的。
foo.x
或者如果你坚持可以用Foo.x
替代Foo().x
);使用类属性和静态/类方法(Foo.x
)。 - Chris Morgan