【发布时间】:2011-10-09 07:05:01
【问题描述】:
这个问题不是为了讨论singleton design pattern 是否可取、是否是反模式或任何宗教战争,而是讨论如何在 Python 中最好地实现这种模式这是最pythonic的方式。在这种情况下,我将“最pythonic”定义为表示它遵循“最小惊讶原则”。
我有多个类会变成单例(我的用例是记录器,但这并不重要)。当我可以简单地继承或装饰时,我不希望用额外的口香糖来弄乱几个类。
最佳方法:
方法一:装饰器
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 本身是一个函数,而不是类,因此您不能从中调用类方法。也适用于
x = 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__可以在从第二个基类继承期间被覆盖?人们必须考虑的不仅仅是必要的。
方法三:metaclass
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__的类。
方法5:一个模块
一个模块文件singleton.py
优点
- 简单胜于复杂
缺点
- 不延迟实例化
【问题讨论】:
-
另外三种技术:使用模块代替(通常 - 通常,我认为 - 这是 Python 更合适的模式,但它有点取决于你用它做什么);创建一个实例并改为处理它(
foo.x或者如果您坚持使用Foo.x而不是Foo().x);使用类属性和静态/类方法 (Foo.x)。 -
@ChrisMorgan:如果你打算只使用类/静态方法,那么真的不用费心创建一个类。
-
@Cat:效果类似,但是创建全局变量背后的原因几乎可以是任何事情,包括不知道更好。为什么要创建一个单例?如果你不得不问你不应该在这里。这种明确性不仅更加 Pythonic,而且使维护更加简单。是的,单例是全局变量的语法糖,但是类是一大堆难看的东西的语法糖,我认为没有人会告诉你,没有它们你总是会更好。
-
反信号灯的情绪是最糟糕的货运狂热节目。与人们听到(很少有人真正阅读)“Goto 语句被认为有害”并认为 goto 是错误代码的标志(无论上下文如何)的情况相同。
标签: python singleton decorator base-class metaclass