这个问题不是为了讨论是否需要单例设计模式,是否是反模式,还是针对任何宗教战争,而是要讨论如何以最pythonic的方式在Python中最好地实现此模式。在这种情况下,我将“最pythonic”定义为表示它遵循“最少惊讶的原理”。
我有多个将成为单例的类(我的用例用于记录器,但这并不重要)。当我可以简单地继承或修饰时,我不希望增加gumph来使几个类杂乱无章。
最佳方法:
方法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
优点
- 装饰器的添加方式通常比多重继承更直观。
缺点
- 使用MyClass()创建的对象将是真正的单例对象,而MyClass本身是一个函数,而不是类,因此您不能从中调用类方法。也就
m = MyClass(); n = MyClass(); o = type(n)();
这样m == n && m != o && n != o
方法2:一个基类
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__
从第二个基类继承期间可能被覆盖?人们必须思考的超出了必要。
方法3:元类
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__
它的正确用途(使我意识到这一点)
缺点
- 有吗
方法4:装饰器返回具有相同名称的类
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__
。
方法5:一个模块
一个模块文件 singleton.py
优点
- 简单胜于复杂
缺点
foo.x
或者,如果您坚持使用Foo.x
而不是Foo().x
);使用类属性和静态/类方法(Foo.x
)。