在Python中创建单例

  • 问题:
  • 这个问题是否是为了讨论singleton design pattern是理想的,是一种反模式,或者是任何宗教战争,但是讨论一下如何以最具Python风格的方式在Python中实现这个模式。在这个例子中,我定义“最Python”是指它遵循“最小惊奇原则”

    我有多个类将成为单例(我的用例是针对一个记录器,但这并不重要)。当我可以简单地继承或装饰时,我不希望在几个类中添加gumph

    最佳方法:

    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

    专业人士

    缺点

    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

    专业人士

    缺点

    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

    专业人士

    缺点

    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

    专业人士

    缺点

    a module file辛格顿专业人士

    缺点

  • 答案:
  • 我推荐方法2,但是使用元类比基类更好。下面是一个示例实现:

    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]

    class Logger(object):
    __metaclass__ = Singleton

    或者在Python3中

    class Logger(metaclass=Singleton):
    pass

    如果您想在每次调用类时都运行\uyu init\,请添加

            else:
    cls._instances[cls].__init__(*args, **kwargs)

    调用Singleton中的if语句

    关于元类的几句话。元类是类的;也就是说,类是其元类的实例。您可以在Python中找到一个对象的元类,它具有type(obj)。普通的新样式类属于类型。上面代码中的记录器类型为_模块。单例“,正如Logger的(唯一)实例将是class”类型_模块。记录器'。当您使用logger()调用logger时,Python首先会询问loggerSingleton的元类该做什么,从而允许预先创建实例。这个过程与Python通过执行myclass.attribute

    元类本质上决定了类的定义意味着什么以及如何实现该定义。参见示例http://code.activestate.com/recipes/498149/,这实际上是使用元类在Python中重新创建C风格的structs。线程元类的一些(具体)用例是什么?还提供了一些示例,它们通常与声明性编程有关,尤其是在ORMs中使用的示例

    在这种情况下,如果使用方法2,并且子类定义了一个新的方法,则每次调用SubClassOfSingleton()时,它都会被执行,因为它负责调用返回存储实例的方法。对于元类,当创建唯一的实例时,只调用一次。您希望自定义调用类的含义,这取决于类的类型

    一般来说,使用元类来实现单例是有意义的。单例是特殊的,因为只创建一次,而元类是您自定义类的创建的方式。如果您需要以其他方式自定义单例类定义,那么使用元类可以让您获得更多的控制权

    您的singleton不需要多重继承(因为元类不是基类),但是对于使用多重继承的已创建类的子类,您需要确保singleton类是第一个/最左边的类,它的元类重新定义了\uuku call不太可能是个问题。实例dict不在实例的命名空间中,因此它不会意外地覆盖它

    您还将听到singleton模式违反了“单一责任原则”——每个类只应做一件事。这样你就不必担心在你需要改变另一件事情的时候把代码搞乱,因为它们是独立的和封装的。元类实现通过了这个测试。元类负责执行模式,创建的类和子类不需要意识到它们是单例的方法1未能通过测试,正如您在“MyClass本身是一个函数,而不是一个类,因此您不能从它调用类方法”中指出的那样

    编写既能在Python2和Python2中运行的东西,需要使用稍微复杂一些的方案。由于元类通常是类型类型的子类,因此可以使用元类在运行时动态创建一个中间基类,并将其作为其元类,然后将用作publicSingleton基类的基类。解释起来比做起来难,如下所示:

    # works in Python 2 & 3
    class _Singleton(type):
    """ A metaclass that creates a Singleton base class when called. """
    _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]

    class Singleton(_Singleton('SingletonMeta', (object,), {})): pass

    class Logger(Singleton):
    pass

    这种方法的一个讽刺的方面是它使用子类化来实现一个元类。一个可能的优点是,与纯元类不同,isinstance(inst,Singleton)将返回True

    在另一个主题中,您可能已经注意到了这一点,但是您在原始文章中的基类实现是错误的。\u实例需要在类上引用,需要使用super()或者递归,\uu new实际上是一个静态方法,必须将类传递给,而不是类方法,因为实际的类尚未在调用时创建。所有这些对于元类实现也是如此

    class Singleton(object):
    _instances = {}
    def __new__(class_, *args, **kwargs):
    if class_ not in class_._instances:
    class_._instances[class_] = super(Singleton, class_).__new__(class_, *args, **kwargs)
    return class_._instances[class_]

    class MyClass(Singleton):
    pass

    c = MyClass()

    我本来是在写评论,但是太长了,所以我在这里加上这个。Method#4比其他decorator版本更好,但它的代码比单个实例所需的代码多,而且它的作用也不清楚

    主要的问题源于这个类是它自己的基类。首先,一个类是一个几乎完全相同的类的子类,它的名称只存在于它的属性中,这不是很奇怪吗?这也意味着您不能用super()来定义任何调用基类上同名方法的方法,因为它们将递归。这意味着你的类不能自定义\uunew_优,也不能从任何需要调用\uyu init_优的类派生

    您的用例是想要使用单例的一个更好的例子。你在其中一条评论中说:“对我来说,日志记录一直是单身人士的自然选择。”你说得对极了

    当人们说单例不好时,最常见的原因是它们是隐式共享状态。虽然全局变量和顶层模块导入是显式的共享状态,但传递的其他对象通常是实例化的。这是一个很好的观点,除了两个例外

    第一个,也是在很多地方都提到过的一个问题,就是单粒子是常数。使用全局常量,尤其是枚举,是被广泛接受的,并且被认为是明智的,因为无论发生什么,没有一个用户可以为其他用户把它们弄乱。对于一个恒定的单粒子来说也是如此

    第二个例外是相反的——当singleton只是一个数据接收器,而不是一个数据源(直接或间接)。这就是为什么伐木工人觉得自己是“自然”地使用单身汉。由于不同的用户没有以其他用户关心的方式更改记录器,因此存在不真正共享状态。这就否定了反对singleton模式的主要论点,并使它们成为一个合理的选择,因为它们对任务的易用性

    以下是引用http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html公司名称:

    现在,有一种单粒子是可以的。这是一个所有可到达的对象都是不可变的。如果所有对象都是不变的,那么Singleton就没有全局状态,因为一切都是常量。但要把这种单子转化成可变的单子是很容易的,这是非常滑的。所以,我也反对这些单身汉,不是因为他们不好,而是因为他们很容易变坏。(顺便说一句,Java枚举就是这类单例。只要不在枚举中输入state,就可以了,所以请不要。)

    另一种是半可接受的单例类型,它们不会影响代码的执行,它们没有“副作用”。日志记录就是一个完美的例子。它加载了singleton和global state。这是可以接受的(因为它不会伤害您),因为无论是否启用给定的记录器,应用程序的行为没有任何不同。这里的信息是单向流动的:从应用程序流向记录器。即使日志记录器是全局状态,因为没有信息从记录器流到应用程序中,日志记录器也是可以接受的。如果您想让您的测试断言某个东西正在被记录,那么您仍然应该注入您的记录器,但是一般来说,尽管日志记录器已满状态,但它并不是有害的