这个问题不是为了讨论单例设计模式是否可取、是否是一种反模式,或者是否用于任何宗教战争,而是为了讨论如何以最Python化的方式在Python中最好地实现这种模式。在这个例子中,我定义“最蟒蛇”是指它遵循“最少惊讶的原则”。

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

最佳方法:


方法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

Pros

装饰符的添加方式通常比多重继承更直观。

Cons

虽然使用MyClass()创建的对象将是真正的单例对象,但MyClass本身是一个函数,而不是类,因此不能从中调用类方法x=MyClass();y=MyClass();t=类型(n)();

则x==y但x!=t&y!=吨


方法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

Pros

这是一门真正的课

Cons

多重继承-嗯__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

Pros

这是一门真正的课自动神奇地覆盖继承为正确的目的使用__metaclass__(并让我意识到)

Cons

有吗?


方法4:decorator返回同名类

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

Pros

这是一门真正的课自动神奇地覆盖继承

Cons

创建每个新类是否没有开销?在这里,我们为每个希望成为单例的类创建两个类。虽然这对我来说很好,但我担心这可能无法扩展。当然,对于这种模式的规模是否太容易,还有争议。。。_seled属性的意义是什么无法使用super()在基类上调用同名方法,因为它们将递归。这意味着您不能自定义__new__,也不能对需要调用__init__的类进行子类化。


方法5:模块

模块文件singleton.py

Pros

简单胜于复杂

Cons

未延迟实例化


当前回答

经过一段时间的努力,我终于想出了以下方法,这样当从单独的模块调用时,配置对象只能加载一次。元类允许将全局类实例存储在内置dict中,目前看来这是存储适当程序全局的最整洁的方式。

import builtins

# -----------------------------------------------------------------------------
# So..... you would expect that a class would be "global" in scope, however
#   when different modules use this,
#   EACH ONE effectively has its own class namespace.  
#   In order to get around this, we use a metaclass to intercept
#   "new" and provide the "truly global metaclass instance" if it already exists

class MetaConfig(type):
    def __new__(cls, name, bases, dct):
        try:
            class_inst = builtins.CONFIG_singleton

        except AttributeError:
            class_inst = super().__new__(cls, name, bases, dct)
            builtins.CONFIG_singleton = class_inst
            class_inst.do_load()

        return class_inst

# -----------------------------------------------------------------------------

class Config(metaclass=MetaConfig):

    config_attr = None

    @classmethod
    def do_load(cls):
        ...<load-cfg-from-file>...

其他回答

这个答案可能不是你想要的。我想要一个单例,因为只有那个对象才有它的身份,以便与之比较。在我的例子中,它被用作Sentinel Value。答案很简单,将任何对象设为mything=object(),根据python的性质,只有该对象才具有其身份。

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration

如果要将实例用作属性,可以使用元类。例如

class SingletonMeta(type):
    def __init__(cls, *args, **kwargs):
        super().__init__(*args, **kwargs)
        cls._instance = None
        cls._locker = threading.Lock()

    @property
    def instance(self, *args, **kwargs):
        if self._instance is None:
            with self._locker:
                if self._instance is None:
                    self._instance = self(*args, **kwargs)
        return self._instance


class MyClass(metaclass=SingletonMeta):
    def __init__(self):
        # init here
        pass


# get the instance
my_class_instance = MyClass.instance

这个解决方案在模块级别造成了一些命名空间污染(三个定义而不是一个),但我发现很容易理解。

我希望能够编写这样的东西(惰性初始化),但不幸的是,类在它们自己的定义体中不可用。

# wouldn't it be nice if we could do this?
class Foo(object):
    instance = None

    def __new__(cls):
        if cls.instance is None:
            cls.instance = object()
            cls.instance.__class__ = Foo
        return cls.instance

由于这是不可能的,我们可以在

Eagle初始化:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        return foo_instance


foo_instance = FooMaker()
foo_instance.__class__ = Foo

延迟初始化:

Eagle初始化:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        global foo_instance
        if foo_instance is None:
            foo_instance = FooMaker()
        return foo_instance


foo_instance = None

方法:一次性使用后重写__new__

class Singleton():
    def __init__(self):
        Singleton.instance = self
        Singleton.__new__ = lambda _: Singleton.instance

Pros

极其简洁真正的类,不需要模块正确使用lambda和蟒猴补丁

Cons

__new__可能再次被覆盖

这个怎么样:

def singleton(cls):
    instance=cls()
    cls.__new__ = cls.__call__= lambda cls: instance
    cls.__init__ = lambda self: None
    return instance

将其用作应该是单例的类的装饰器。这样地:

@singleton
class MySingleton:
    #....

这类似于另一个答案中的singleton=lambda c:c()修饰符。与其他解决方案一样,唯一的实例具有类名(MySingleton)。然而,使用此解决方案,您仍然可以通过执行MySingleton()从类中“创建”实例(实际上是唯一的实例)。它还防止您通过执行类型(MySingleton)()(也返回相同的实例)来创建其他实例。