这个问题不是为了讨论单例设计模式是否可取、是否是一种反模式,或者是否用于任何宗教战争,而是为了讨论如何以最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

未延迟实例化


当前回答

代码基于Tolli的答案。

#decorator, modyfies new_cls
def _singleton(new_cls):
    instance = new_cls()                                              #2
    def new(cls):
        if isinstance(instance, cls):                                 #4
            return instance
        else:
            raise TypeError("I can only return instance of {}, caller wanted {}".format(new_cls, cls))
    new_cls.__new__  = new                                            #3
    new_cls.__init__ = lambda self: None                              #5
    return new_cls


#decorator, creates new class
def singleton(cls):
    new_cls = type('singleton({})'.format(cls.__name__), (cls,), {} ) #1
    return _singleton(new_cls)


#metaclass
def meta_singleton(name, bases, attrs):
    new_cls = type(name, bases, attrs)                                #1
    return _singleton(new_cls)

说明:

创建新类,继承给定的cls(如果有人想要例如singleton(list),它不会修改cls)创建实例。在覆盖__new__之前,这很简单。现在,当我们轻松创建实例时,使用刚才定义的方法重写__new__。该函数仅在调用方期望的情况下返回实例,否则引发TypeError。当某人试图从修饰类继承时,该条件不满足。如果__new__()返回cls的一个实例,那么新实例的__init__()方法将像__init__一样被调用(self[,…]),其中self是新实例,其余参数与传递给__new__的参数相同。实例已初始化,因此函数将__init__替换为不执行任何操作的函数。

看到它在线工作

其他回答

这个答案可能不是你想要的。我想要一个单例,因为只有那个对象才有它的身份,以便与之比较。在我的例子中,它被用作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

方法3看起来很整洁,但如果您希望程序同时在Python2和Python3中运行,它就不起作用了。即使使用Python版本的测试来保护单独的变体也失败了,因为Python 3版本在Python 2中给出了语法错误。

感谢Mike Watkins:http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/.如果您希望程序同时在Python 2和Python 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]

MC = Singleton('MC', (object), {})

class MyClass(MC):
    pass    # Code for the class implementation

我假设赋值中的“object”需要替换为“BaseClass”,但我还没有尝试过(我已经尝试了如图所示的代码)。

也许我误解了单例模式,但我的解决方案是简单实用的(Python?)。这段代码实现了两个目标

使Foo的实例可以在任何地方访问(全局)。Foo只能存在一个实例。

这就是代码。

#!/usr/bin/env python3

class Foo:
    me = None

    def __init__(self):
        if Foo.me != None:
            raise Exception('Instance of Foo still exists!')

        Foo.me = self


if __name__ == '__main__':
    Foo()
    Foo()

输出

Traceback (most recent call last):
  File "./x.py", line 15, in <module>
    Foo()
  File "./x.py", line 8, in __init__
    raise Exception('Instance of Foo still exists!')
Exception: Instance of Foo still exists!

使用类变量(无修饰符)

通过重写__new__方法返回类的相同实例。仅首次初始化类的布尔值:

class SingletonClass:
    _instance = None

    def __new__(cls, *args, **kwargs):
        # If no instance of class already exits
        if cls._instance is None:
            cls._instance = object.__new__(cls)
            cls._instance._initialized = False
        return cls._instance
        
    def __init__(self, *args, **kwargs):
        if self._initialized:
            return

        self.attr1 = args[0]
        # set the attribute to `True` to not initialize again
        self._initialized = True

这是给你的一句话:

singleton = lambda c: c()

以下是您的使用方法:

@singleton
class wat(object):
    def __init__(self): self.x = 1
    def get_x(self): return self.x

assert wat.get_x() == 1

您的对象会被急切地实例化。这可能是你想要的,也可能不是你想要的。