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

未延迟实例化


当前回答

赞成的意见这是一个真正的类Auto神奇地覆盖继承使用元类出于正当目的(并使我意识到)有吗?

这将是串行化的问题。如果您尝试从文件(pickle)反序列化对象,它将不使用__call__,因此将创建新文件,您可以使用__new__的基类继承来防止这种情况。

其他回答

如果想要拥有同一类的多个实例,但只有当args或kwargs不同时,才可以使用第三方python包Handy Decorator(包装饰器)。前任。如果您有一个处理串行通信的类,并且要创建一个实例,您希望将串行端口作为参数发送,那么使用传统的方法是行不通的如果参数不同,可以使用上面提到的修饰符创建类的多个实例。对于相同的参数,decorator将返回已创建的相同实例。

>>> from decorators import singleton
>>>
>>> @singleton
... class A:
...     def __init__(self, *args, **kwargs):
...         pass
...
>>>
>>> a = A(name='Siddhesh')
>>> b = A(name='Siddhesh', lname='Sathe')
>>> c = A(name='Siddhesh', lname='Sathe')
>>> a is b  # has to be different
False
>>> b is c  # has to be same
True
>>>

与从元类派生相比,我更喜欢decorator语法。我的两分钱:

from typing import Callable, Dict, Set


def singleton(cls_: Callable) -> type:
    """ Implements a simple singleton decorator
    """
    class Singleton(cls_):  # type: ignore
        __instances: Dict[type, object] = {}
        __initialized: Set[type] = set()

        def __new__(cls, *args, **kwargs):
            if Singleton.__instances.get(cls) is None:
                Singleton.__instances[cls] = super().__new__(cls, *args, **kwargs)
            return Singleton.__instances[cls]

        def __init__(self, *args, **kwargs):
            if self.__class__ not in Singleton.__initialized:
                Singleton.__initialized.add(self.__class__)
                super().__init__(*args, **kwargs)

    return Singleton


@singleton
class MyClass(...):
    ...

这比其他装饰器有一些好处:

isinstance(MyClass(),MyClass)仍然有效(从子句返回函数而不是类将使isinstance失败)属性、classmethod和staticmethod仍将按预期工作__init__()构造函数只执行一次您可以再次使用@singleton从修饰类(无用?)继承

欺骗:

打印(MyClass()__第__类__name__)将返回Singleton而不是MyClass。如果您仍然需要这个,我建议使用上面建议的元类。

如果您需要基于构造函数参数的不同实例,则需要改进此解决方案(由siddhesh suhas sathe提供的解决方案)。

最后,正如其他人所建议的,考虑在python中使用模块。模块是对象。您甚至可以在变量中传递它们,并将它们注入到其他类中。

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

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

# 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

查看堆栈溢出问题是否有一种简单、优雅的方法来定义Python中的单体?有几种解决方案。

我强烈建议观看Alex Martelli关于python中设计模式的演讲:第1部分和第2部分。特别是,在第1部分中,他谈到了单态/共享状态对象。

您只需要一个装饰器,具体取决于python版本:


Python 3.2+

实施

from functools import lru_cache

@lru_cache(maxsize=None)
class CustomClass(object):

    def __init__(self, arg):
        print(f"CustomClass initialised with {arg}")
        self.arg = arg

用法

c1 = CustomClass("foo")
c2 = CustomClass("foo")
c3 = CustomClass("bar")

print(c1 == c2)
print(c1 == c3)

输出

>>> CustomClass initialised with foo
>>> CustomClass initialised with bar
>>> True
>>> False

注意foo只打印一次


Python 3.9+

实施:

from functools import cache

@cache
class CustomClass(object):
    ...