在Python中创建单例

the*_*oom 829 python singleton metaclass decorator base-class

这个问题不是讨论单身人士设计模式是否可取,反模式,还是任何宗教战争,而是讨论如何以最蟒蛇的方式在Python中最好地实现这种模式.在这种情况下,我将"最pythonic"定义为表示它遵循"最小惊讶原则".

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

最好的方法:


方法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
Run Code Online (Sandbox Code Playgroud)

优点

  • 装饰器的添加方式通常比多重继承更直观.

缺点

  • 虽然使用MyClass()创建的对象将是真正的单例对象,但MyClass本身是一个函数,而不是一个类,因此您无法从中调用类方法.也是为了m = MyClass(); n = MyClass(); o = type(n)();那时m == n && m != o && n != o

方法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
Run Code Online (Sandbox Code Playgroud)

优点

  • 这是一个真正的课程

缺点

  • 多重继承 - eugh!__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
Run Code Online (Sandbox Code Playgroud)

优点

  • 这是一个真正的课程
  • 自动神奇地涵盖了继承
  • 使用__metaclass__它的正确用途(使我意识到这一点)

缺点

  • 有吗?

方法4:装饰器返回一个具有相同名称的类

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
Run Code Online (Sandbox Code Playgroud)

优点

  • 这是一个真正的课程
  • 自动神奇地涵盖了继承

缺点

  • 是否没有创建每个新类的开销?在这里,我们为每个要创建单例的类创建两个类.虽然这在我的情况下很好,但我担心这可能无法扩展.当然,关于是否要过于容易地扩展这种模式存在争议......
  • _sealed属性的重点是什么?
  • 无法在基类上调用相同名称的方法,super()因为它们会递归.这意味着您无法自定义__new__并且无法子类化需要您调用的类__init__.

agf*_*agf 590

使用元类

我建议使用方法#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
Run Code Online (Sandbox Code Playgroud)

或者在Python3中

class Logger(metaclass=Singleton):
    pass
Run Code Online (Sandbox Code Playgroud)

如果要在__init__每次调用类时运行,请添加

        else:
            cls._instances[cls].__init__(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

if声明中Singleton.__call__.

关于元类的几句话.元类是类的类 ; 也就是说,类是其元类实例.你可以在Python中找到一个对象的元类type(obj).普通的新式类是类型type.Logger在上面的代码中将是类型class 'your_module.Singleton',就像(唯一)实例Logger将是类型class 'your_module.Logger'.当你调用记录仪与Logger(),Python的首先要求的元类Logger,Singleton,做什么,允许实例创建是捷足先登.这个过程与Python一样,__getattr__当你通过调用引用它的一个属性时,通过调用来询问类做什么myclass.attribute.

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

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

通常,使用元类来实现单例是有意义的.单例是特殊的,因为只创建一次,而元类是自定义类创建的方式.如果需要以其他方式自定义单例类定义,则使用元类可以提供更多控制.

你的单例不需要多重继承(因为元类不是基类),但是对于使用多重继承的创建类的子类,你需要确保单例类是第一个/最左边的一个带有重新定义的元类的类.__call__这不太可能成为一个问题.实例dict 不在实例的命名空间中,因此不会意外覆盖它.

你还会听到单身人士模式违反了"单一责任原则" - 每个班级应该只做一件事.这样,如果您需要更改另一个代码,您不必担心代码会弄乱一件事,因为它们是分开的并且是封装的.元类实现通过了此测试.元类负责执行模式,并且创建的类和子类不需要知道它们是单例.方法#1未通过此测试,因为您注意到"MyClass本身是一个函数,而不是一个类,因此您无法从中调用类方法."

Python 2和3兼容版本

编写适用于Python2和3的东西需要使用稍微复杂的方案.由于元类通常是类型的子类type,因此可以使用它来在运行时动态创建中间基类,并将其作为其元类,然后将用作公共Singleton基类的基类.这比解释更难解释,如下图所示:

# 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
Run Code Online (Sandbox Code Playgroud)

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

更正

在另一个主题上,您可能已经注意到了这一点,但原始帖子中的基类实现是错误的._instances需要在类引用,你需要使用super()或者你正在递归,并且__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()
Run Code Online (Sandbox Code Playgroud)

装饰师返回班级

我最初写的是评论,但是时间太长了,所以我会在这里添加.方法#4比其他装饰器版本更好,但是它比单例所需的代码更多,而且它的功能并不清楚.

主要问题源于该类是它自己的基类.首先,让一个类成为一个几乎完全相同的类的子类,并且只存在于其__class__属性中,这是不是很奇怪?这也意味着你不能定义调用同名的方法对它们的基类的任何方法super(),因为他们会重复.这意味着您的类无法自定义__new__,也无法从需要__init__调用它们的任何类派生.

何时使用单例模式

您的用例是想要使用单例的更好示例之一.你在其中一条评论中说:"对我而言,伐木似乎一直是单身人士的天生候选人." 你说得.

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

第一个,也就是各个地方提到的一个,就是单身人士不变的时候.使用全局常量,特别是枚举,被广泛接受,并且被认为是理智的,因为无论如何,没有任何用户可以将它们搞砸到任何其他用户.对于常数单例也是如此.

第二个例外,提到的更少,恰恰相反 - 当单例只是数据接收器而不是数据源(直接或间接)时.这就是伐木工人对单身人士的"自然"用途的原因.由于各种用户没有以其他用户关心的方式更改记录器,因此没有真正的共享状态.这否定了针对单例模式的主要论点,并且因为它们易于用于任务而使它们成为合理的选择.

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

现在,有一种Singleton可以.这是一个单例,其中所有可到达的对象都是不可变的.如果所有对象都是不可变的,那么Singleton没有全局状态,因为一切都是常量.但是很容易将这种单体变成可变的单体,它是非常滑的斜率.因此,我也反对这些单身人士,不是因为他们不好,而是因为他们很容易变坏.(作为旁注,Java枚举只是这些单例.只要你没有将状态放入枚举中就可以了,所以请不要.)

另一种半单可接受的单身人士是那些不影响你的代码执行的人,他们没有"副作用".记录是完美的例子.它充满了单身人士和全球状态.这是可以接受的(因为它不会伤害你),因为无论是否启用给定的记录器,您的应用程序都不会有任何不同.这里的信息以一种方式流动:从您的应用程序到记录器.即使记录器是全局状态的,因为没有信息从记录器流入您的应用程序,因此记录器是可接受的.如果您希望测试断言某些内容已被记录,您仍应注入记录器,但一般情况下,尽管状态已满,但记录器无害.

  • 请访问http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html.它通常是反单身人士(有充分的理由),但它有一个很好的解释为什么不可变的单身人士和没有副作用的单身人士没有同样的问题,如果你小心的话.我将在帖子的末尾引用一点. (9认同)
  • 不,单身人士永远不会好.记录可能是一个全球性的好候选人(尽管它们很糟糕),但肯定不是单身人士. (5认同)
  • 单身人士的问题是"只有一个实例"的愚蠢前提.这和吨线程安全问题.和依赖隐藏.全局都很糟糕,而单身人士只是全球性问题. (4认同)
  • @Cat单身人士有很好的用途.硬件模块的懒惰实例化(特别是在单线程应用程序中)是其中之一(但也存在线程安全的单例). (4认同)
  • 元类中的@Alcott`__new__`是_class_是新的 - 当它被定义时,而不是_instance_是新的时候.调用类(`MyClass()`)是要覆盖的操作,而不是类的定义.如果您真的想了解Python的工作原理,那么您可以做的最好的事情(除了继续使用它之外)是http://docs.python.org/reference/datamodel.html.关于元类的一个很好的参考是http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/.关于单身人士的一篇很好的文章是我在这个答案中链接的谷歌博客系列. (3认同)
  • @agf,通过元类实现Singleton时,为什么使用“ __call__”而不是“ __new__”?和有什么区别? (2认同)
  • 在 `__call__` 函数中使用 `Singletone._instances` 比使用 `cls._instances` 更好吗?否则,“Singletone”的“类”可能会定义自己的类变量“_instances”,并与“Singletone”冲突。 (2认同)
  • 如何使用 Singleton 类来管理硬件组件?想象一下,您有 LCD 显示屏(例如在您的 RPi 上)并且您编写了一个名为 displayManager.py 的小型 Python 脚本。我认为它完全符合此处提到的“最常见的原因是它们是隐式共享状态”的定义。如果许多其他组件正在使用此 LCD 显示器,则它们以某种方式使用共享状态。为什么不通过 Singleton 类这样做。 (2认同)
  • @MikeC因为这样你就可以拥有多个不同的单例子类,每个子类都有一个实例,而不是所有子类只有一个实例。 (2认同)

Cat*_*lus 85

class Foo(object):
     pass

some_global_variable = Foo()
Run Code Online (Sandbox Code Playgroud)

模块只导入一次,其他一切都是过度思考.不要使用单身并尽量不使用全局变量.

  • 为什么你说"不要使用单身人士"?任何原因? (13认同)
  • 我使用“import ABC as X”在几个不同的脚本文件(所有这些脚本文件都使用“单例”)中导入了该模块,并且实际上为每个具有“单例”的“import”语句的脚本导入了一次该模块模块。因此,每次导入模块时,我定义的变量都会重新分配其值。我还尝试删除别名,甚至将其导入项目的每个文件中,结果相同。我使用的是Python 3.9.6。具有元类的单例(来自 agf 的答案)为我的用例(日志记录,使用必须在编译时初始化一次的变量)完美地实现了这一点 (5认同)
  • 如果必须对单身人员进行腌制,这将不起作用.使用你给出的例子:`s = some_global_variable; str = pickle.dumps(s); s1 = pickle.loads(str); print s是s1; #False` (3认同)
  • @dividebyzero:`is`运算符测试指针相等性.如果`pickle.loads`返回对预先存在的对象的引用而不是对新创建的对象的引用,我会感到非常惊讶 - 这就是把它称为bug.因此,测试s是否为s1`并没有告诉你任何关于使用模块作为单例的适用性. (3认同)
  • 当实例化需要时间(例如连接到数据库)时,这也会出现问题。这会减慢导入时整个程序的执行速度。 (3认同)
  • @ leo-the-manic:公平点; 然而,这只是Python实现对象"True","False"和"None"的副作用,与"pickle.loads"背后的代码无关.此外,仅对只读对象执行安全操作.如果`pickle.loads`将返回对已存在的*可修改*对象的引用 - 例如模块 - 这将是一个错误.(所以我坚持我的意思,除了divbyzero的代码示例没有任何证据.) (2认同)
  • 这仅在所有导入以相同方式发生时才有效。`import project.module` 和 `import .module` 将运行代码两次。 (2认同)
  • 如果在 some_module.py 中使用了 some_global_variable 并且那是被测模块,那么可以模拟 some_global_variable 吗? (2认同)
  • `Foo` 应该是 `__Foo` 以便用户不会随意使用它吗? (2认同)
  • 这是一个非常模糊的答案,我可以建议根据讨论完全重新编辑它。 (2认同)

war*_*iuc 65

使用模块.它只导入一次.在其中定义一些全局变量 - 它们将是单例的'属性'.添加一些函数 - 单例的'方法'.

  • 如果你可以基于其他类,那么它可能不是单身.你可以创建一个派生类,但也可以创建一个基类,但派生类也是基类的成员,你有两个基础,你应该使用哪一个? (16认同)
  • 所以你最终得到的是......不是一个阶级.你不能将它作为一个类使用,你不能将其他类基于它,你使用导入语法,突然间你失去了OOP的所有好处...... (11认同)
  • 如何在模块中使用参数初始化单例对象? (3认同)

小智 23

你可能永远不需要Python中的单例.只需在模块中定义所有数据和功能,您就拥有了一个事实上的单例.

如果你真的必须有一个单身课程,那么我会选择:

class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()
Run Code Online (Sandbox Code Playgroud)

使用:

from mysingleton import my_singleton
my_singleton.foo()
Run Code Online (Sandbox Code Playgroud)

其中mysingleton.py是您定义My_Singleton的文件名.这是有效的,因为在第一次导入文件后,Python不会重新执行代码.

  • 大多数情况下都是如此,但有时这还不够.例如,我有一个需要在DEBUG级别记录许多类的实例化的项目.我需要在启动时解析的命令行选项,以便设置用户指定的日志记录级别_before_这些类是实例化的.模块级实例化使这成为问题.有可能我可以仔细构建应用程序,以便所有这些类在CLI处理完成之前不会被导入,但我的应用程序的自然结构比教条坚持"单身人士不好"更重要,因为他们可以做得很干净. (3认同)

Ces*_*aro 19

你只需要一个装饰器,具体取决于 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
Run Code Online (Sandbox Code Playgroud)

用法

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

print(c1 == c2)
print(c1 == c3)
Run Code Online (Sandbox Code Playgroud)

输出

>>> CustomClass initialised with foo
>>> CustomClass initialised with bar
>>> True
>>> False
Run Code Online (Sandbox Code Playgroud)

注意如何foo只打印一次


Python 3.9+

执行

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

print(c1 == c2)
print(c1 == c3)
Run Code Online (Sandbox Code Playgroud)

  • 术语 [singleton](https://en.wikipedia.org/wiki/Singleton_pattern) 描述了它只有一个实例。考虑一个较大的应用程序,它在“分支”中使用该模式,并且您意外地在内存中保留了一个巨大的类。我并不是说你的解决方案是“不”,但应该在某处提及。 (2认同)

Jon*_*ker 15

这是你的单行:

singleton = lambda c: c()
Run Code Online (Sandbox Code Playgroud)

以下是您使用它的方式:

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

assert wat.get_x() == 1
Run Code Online (Sandbox Code Playgroud)

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

  • 维基百科:"单例模式是一种设计模式,它将类的实例化限制为一个对象".我会说我的解决方案就是这样做的.好吧,我想有人可以做`wat2 = type(wat)()`,但这是python,我们都同意成年人和所有这些.你不能*保证*只有一个实例,但你可以保证,如果人们制作第二个实例,它会看起来很丑陋 - 如果他们是体面的,正直的人 - 就像他们的警告标志.我错过了什么? (7认同)
  • -1.这非常糟糕而且很愚蠢.您没有定义要用作对象的类.你不能再像`type(wat)`或`wat .__ class__`那样非常难看.如果你真的想要实现这一点,那么最好定义类并立即实例化它,不需要搞砸装饰器. (4认同)
  • 为什么需要知道使用单例的类?只需使用单例对象即可。 (2认同)

小智 9

  • 如果想要拥有同一类的多个实例,但前提是 args 或 kwargs 不同,则可以使用第三方 python 包 Handy Decorators (package decorators)。
  • 前任。
    1. 如果您有一个处理通信的类serial,并且要创建一个想要将串行端口作为参数发送的实例,那么使用传统方法将不起作用
    2. 使用上面提到的装饰器,如果参数不同,可以创建该类的多个实例。
    3. 对于相同的参数,装饰器将返回已创建的相同实例。
>>> 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
>>>
Run Code Online (Sandbox Code Playgroud)


Rub*_*rop 8

使用函数属性也很简单

def f():
    if not hasattr(f, 'value'):
        setattr(f, 'value', singletonvalue)
    return f.value
Run Code Online (Sandbox Code Playgroud)


Ant*_*ton 7

查看Stack Overflow问题是否有一种简单,优雅的方法来定义Python中的单例?有几个解决方案.

我强烈建议观看Alex Martelli关于python设计模式的讨论:第1 部分第2部分.特别是,在第1部分中,他讨论了单身人士/共享状态对象.

  • 虽然这并不是我问题的真正答案,但您指向的资源非常有用。我不情愿地给你+1 (2认同)

Pau*_*nta 7

这是我自己的单例实现。你所要做的就是装饰班级;要获得单例,您必须使用该Instance方法。这是一个例子:

@Singleton
class Foo:
    def __init__(self):
        print 'Foo created'

f = Foo() # Error, this isn't how you get the instance of a singleton

f = Foo.Instance() # Good. Being explicit is in line with the Python Zen
g = Foo.Instance() # Returns already created instance

print f is g # True
Run Code Online (Sandbox Code Playgroud)

这是代码:

class Singleton:
    """
    A non-thread-safe helper class to ease implementing singletons.
    This should be used as a decorator -- not a metaclass -- to the
    class that should be a singleton.

    The decorated class can define one `__init__` function that
    takes only the `self` argument. Other than that, there are
    no restrictions that apply to the decorated class.
 
    To get the singleton instance, use the `Instance` method. Trying
    to use `__call__` will result in a `TypeError` being raised.

    Limitations: The decorated class cannot be inherited from.

    """

    def __init__(self, decorated):
        self._decorated = decorated

    def Instance(self):
        """
        Returns the singleton instance. Upon its first call, it creates a
        new instance of the decorated class and calls its `__init__` method.
        On all subsequent calls, the already created instance is returned.

        """
        try:
            return self._instance
        except AttributeError:
            self._instance = self._decorated()
            return self._instance

    def __call__(self):
        raise TypeError('Singletons must be accessed through `Instance()`.')

    def __instancecheck__(self, inst):
        return isinstance(inst, self._decorated)
Run Code Online (Sandbox Code Playgroud)

  • 这不是真正的单例。`SingletonList = Singleton(list).Instance(); print(SingletonList is type(SingletonList)())` 应该在真正的单例中打印 `True` ;[您的代码打印“False”](http://rextester.com/WIRG69868) (3认同)

小智 7

我会推荐一个使用元类的优雅解决方案

class Singleton(type): 
    # Inherit from "type" in order to gain access to method __call__
    def __init__(self, *args, **kwargs):
        self.__instance = None # Create a variable to store the object reference
        super().__init__(*args, **kwargs)

    def __call__(self, *args, **kwargs):
        if self.__instance is None:
            # if the object has not already been created
            self.__instance = super().__call__(*args, **kwargs) # Call the __init__ method of the subclass (Spam) and save the reference
            return self.__instance
        else:
            # if object (Spam) reference already exists; return it
            return self.__instance

class Spam(metaclass=Singleton):
    def __init__(self, x):
        print('Creating Spam')
        self.x = x


if __name__ == '__main__':
    spam = Spam(100)
    spam2 = Spam(200)
Run Code Online (Sandbox Code Playgroud)

输出:

Creating Spam
Run Code Online (Sandbox Code Playgroud)

从输出中可以看到,只实例化了一个对象


And*_* R. 7

我更喜欢这个解决方案,我发现它非常清晰和直接。例如,它正在使用双重检查,如果其他线程已经创建了它。需要考虑的其他事情是确保反序列化不会创建任何其他实例。 https://gist.github.com/werediver/4396488

import threading


# Based on tornado.ioloop.IOLoop.instance() approach.
# See https://github.com/facebook/tornado
class SingletonMixin(object):
    __singleton_lock = threading.Lock()
    __singleton_instance = None

    @classmethod
    def instance(cls):
        if not cls.__singleton_instance:
            with cls.__singleton_lock:
                if not cls.__singleton_instance:
                    cls.__singleton_instance = cls()
        return cls.__singleton_instance


if __name__ == '__main__':
    class A(SingletonMixin):
        pass

    class B(SingletonMixin):
        pass

    a, a2 = A.instance(), A.instance()
    b, b2 = B.instance(), B.instance()

    assert a is a2
    assert b is b2
    assert a is not b

    print('a:  %s\na2: %s' % (a, a2))
    print('b:  %s\nb2: %s' % (b, b2))
Run Code Online (Sandbox Code Playgroud)

  • 正如我之前提到的,我们需要它来确保,当我们执行“if”并使用锁时,其他一些线程尚未创建此实例 https://en.wikipedia.org/wiki/Double-checked_locking这是面试时被问到的一个相当流行的概念:) (2认同)

DEE*_*ANA 7

使用类变量(无装饰器)

通过重写该__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
Run Code Online (Sandbox Code Playgroud)


soc*_*air 5

from functools import cache

@cache
class xxx:
   ....
Run Code Online (Sandbox Code Playgroud)

简单又有效!

  • 小心 - 仅当您在实例化时不传递任何参数时,这才有效。如果这样做,则会发生缓存未命中,并且最终会得到多个实例。 (2认同)

归档时间:

查看次数:

293100 次

最近记录:

6 年,1 月 前