在Python中实现单例的最佳方式是什么

发布于 2024-11-25 14:00:09 字数 3383 浏览 2 评论 0 原文

这个问题不是为了讨论单例设计模式是否可取,是一种反模式,或者针对任何宗教战争,但讨论如何以最Pythonic的方式在Python中最好地实现这种模式。在这种情况下,我定义“最Pythonic”意味着它遵循“最少惊讶原则”。

我有多个类将成为单例(我的用例是记录器,但这并不重要)。当我可以简单地继承或装饰时,我不希望用添加的口号使多个类变得混乱。

最佳方法:


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

优点

  • 装饰器是附加的,通常比多重继承更直观。

缺点

  • 虽然使用 MyClass() 创建的对象是真正的单例对象,但 MyClass 本身是一个函数,而不是一个类,因此您不能从中调用类方法。也为了

    x = MyClass();
    y = MyClass();
    t = 类型(n)();
    

then x == yx != t && y != t


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

优点

  • 这是一个真正的类

缺点

  • 多重继承 - 呃! __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

优点

  • 这是一个真正的类
  • 自动神奇地覆盖继承
  • 用途__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

优点

  • 这是一个真正的类
  • 自动神奇地覆盖继承

缺点

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

方法 5:一个模块

一个模块文件 singleton.py

优点 简单

  • 胜于复杂

缺点

  • 不延迟实例化

This question is not for the discussion of whether or not the singleton design pattern is desirable, is an anti-pattern, or for any religious wars, but to discuss how this pattern is best implemented in Python in such a way that is most pythonic. In this instance I define 'most pythonic' to mean that it follows the 'principle of least astonishment'.

I have multiple classes which would become singletons (my use-case is for a logger, but this is not important). I do not wish to clutter several classes with added gumph when I can simply inherit or decorate.

Best methods:


Method 1: A decorator

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

  • Decorators are additive in a way that is often more intuitive than multiple inheritance.

Cons

  • While objects created using MyClass() would be true singleton objects, MyClass itself is a function, not a class, so you cannot call class methods from it. Also for

    x = MyClass();
    y = MyClass();
    t = type(n)();
    

then x == y but x != t && y != t


Method 2: A base class

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

  • It's a true class

Cons

  • Multiple inheritance - eugh! __new__ could be overwritten during inheritance from a second base class? One has to think more than is necessary.

Method 3: A metaclass

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

  • It's a true class
  • Auto-magically covers inheritance
  • Uses __metaclass__ for its proper purpose (and made me aware of it)

Cons

  • Are there any?

Method 4: decorator returning a class with the same name

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

  • It's a true class
  • Auto-magically covers inheritance

Cons

  • Is there not an overhead for creating each new class? Here we are creating two classes for each class we wish to make a singleton. While this is fine in my case, I worry that this might not scale. Of course there is a matter of debate as to whether it aught to be too easy to scale this pattern...
  • What is the point of the _sealed attribute
  • Can't call methods of the same name on base classes using super() because they will recurse. This means you can't customize __new__ and can't subclass a class that needs you to call up to __init__.

Method 5: a module

a module file singleton.py

Pros

  • Simple is better than complex

Cons

  • Not lazily instantiated

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

暖树树初阳… 2024-12-02 14:00:09

使用元类

我建议使用方法#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

如果您希望每次调用类时都运行 __init__,请添加

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

Singleton.__call__ 中的 if 语句代码>.

关于元类的几句话。元类是类的类;也就是说,一个类是其元类的一个实例。您可以使用 type(obj) 在 Python 中查找对象的元类。普通的新式类的类型是type。上面代码中的 Logger 的类型为 class 'your_module.Singleton',就像 Logger 的(唯一)实例的类型一样类“your_module.Logger”。当你用Logger()调用logger时,Python首先询问Logger的元类Singleton要做什么,允许实例创建预先完成-清空。此过程与当您通过执行 myclass.attribute 引用某个类的属性时,Python 通过调用 __getattr__ 来询问类要做什么。

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

在这种情况下,如果您使用方法#2,并且子类定义了 __new__ 方法,则每次调用时都会执行< code>SubClassOfSingleton()——因为它负责调用返回存储实例的方法。对于元类,它仅在创建唯一实例时被调用一次。您想要自定义调用类的含义,这是由其类型决定的。

一般来说,使用元类来实现单例是有意义的。单例很特殊,因为它的实例仅创建一次,而元类是您自定义创建类的方式,允许它的行为与普通类不同。如果您需要以其他方式自定义单例类定义,使用元类可以为您提供更多控制

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

您还会听说单例模式违反了“单一职责原则”——每个类应该只做一件事。这样,如果您需要更改代码所做的一件事,就不必担心弄乱另一件事,因为它们是独立且封装的。元类实现通过了此测试。元类负责强制执行模式,并且创建的类和子类不需要知道它们是单例方法 #1 未通过此测试,正如您在“MyClass 本身是一个函数,而不是一个类,因此您无法从中调用类方法”中所指出的那样。

Python 2 和 3 兼容版本

编写在 Python2 和 3 中都可以工作的东西需要使用稍微复杂的方案。由于元类通常是 type 类型的子类,因此可以使用元类在运行时动态创建一个中间基类,并将其作为元类,然后使用that作为基类公共 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

这种方法的一个讽刺之处在于它使用子类化来实现元类。一个可能的优点是,与纯元类不同,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()

装饰器返回一个类

我本来是在写评论,但它太长了,所以我会在这里添加它。 方法 #4 比其他装饰器版本更好,但它的代码比单例所需的代码要多,并且不清楚它的作用。

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

何时使用单例模式

您的用例是想要使用单例的更好的示例之一。您在其中一条评论中说“对我来说,日志记录似乎一直是单例的自然候选者。”你完全正确

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

第一个,也是在很多地方提到的,是当单例恒定时。全局常量(尤其是枚举)的使用被广泛接受,并被认为是明智的,因为无论如何,没有一个用户可以为任何其他用户搞乱它们。对于常量单例来说也是如此。

第二个例外(较少提及)是相反的——当单例只是一个数据接收器,而不是数据源(直接或间接)时。这就是为什么记录器感觉像是单例的“自然”用途。由于各个用户没有以其他用户关心的方式更改记录器,因此存在未真正共享的状态。这否定了反对单例模式的主要论点,并使它们成为合理的选择,因为它们易于使用来完成任务。

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

现在,有一种 Singleton 是可以的。这是一个单例,其中所有可到达的对象都是不可变的。如果所有对象都是不可变的,那么 Singleton 就没有全局状态,因为一切都是不变的。但是很容易将这种单例变成可变单例,这是非常滑坡的。所以,我也反对这些Singleton,不是因为它们不好,而是因为它们很容易变坏。 (顺便说一句,Java 枚举就是这种单例。只要您不将状态放入枚举中就可以,所以请不要这样做。)

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

Use a Metaclass

I would recommend Method #2, but you're better off using a metaclass than a base class. Here is a sample implementation:

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

Or in Python3

class Logger(metaclass=Singleton):
    pass

If you want to run __init__ every time the class is called, add

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

to the if statement in Singleton.__call__.

A few words about metaclasses. A metaclass is the class of a class; that is, a class is an instance of its metaclass. You find the metaclass of an object in Python with type(obj). Normal new-style classes are of type type. Logger in the code above will be of type class 'your_module.Singleton', just as the (only) instance of Logger will be of type class 'your_module.Logger'. When you call logger with Logger(), Python first asks the metaclass of Logger, Singleton, what to do, allowing instance creation to be pre-empted. This process is the same as Python asking a class what to do by calling __getattr__ when you reference one of its attributes by doing myclass.attribute.

A metaclass essentially decides what the definition of a class means and how to implement that definition. See for example http://code.activestate.com/recipes/498149/, which essentially recreates C-style structs in Python using metaclasses. The thread What are some (concrete) use-cases for metaclasses? also provides some examples, they generally seem to be related to declarative programming, especially as used in ORMs.

In this situation, if you use your Method #2, and a subclass defines a __new__ method, it will be executed every time you call SubClassOfSingleton() -- because it is responsible for calling the method that returns the stored instance. With a metaclass, it will only be called once, when the only instance is created. You want to customize what it means to call the class, which is decided by its type.

In general, it makes sense to use a metaclass to implement a singleton. A singleton is special because its instance is created only once, and a metaclass is the way you customize the creation of a class, allowing it to behave differenly than a normal class. Using a metaclass gives you more control in case you need to customize the singleton class definitions in other ways.

Your singletons won't need multiple inheritance (because the metaclass is not a base class), but for subclasses of the created class that use multiple inheritance, you need to make sure the singleton class is the first / leftmost one with a metaclass that redefines __call__ This is very unlikely to be an issue. The instance dict is not in the instance's namespace so it won't accidentally overwrite it.

You will also hear that the singleton pattern violates the "Single Responsibility Principle" -- each class should do only one thing. That way you don't have to worry about messing up one thing the code does if you need to change another, because they are separate and encapsulated. The metaclass implementation passes this test. The metaclass is responsible for enforcing the pattern and the created class and subclasses need not be aware that they are singletons. Method #1 fails this test, as you noted with "MyClass itself is a a function, not a class, so you cannot call class methods from it."

Python 2 and 3 Compatible Version

Writing something that works in both Python2 and 3 requires using a slightly more complicated scheme. Since metaclasses are usually subclasses of type type, it's possible to use one to dynamically create an intermediary base class at run time with it as its metaclass and then use that as the baseclass of the public Singleton base class. It's harder to explain than to do, as illustrated next:

# 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

An ironic aspect of this approach is that it's using subclassing to implement a metaclass. One possible advantage is that, unlike with a pure metaclass, isinstance(inst, Singleton) will return True.

Corrections

On another topic, you've probably already noticed this, but the base class implementation in your original post is wrong. _instances needs to be referenced on the class, you need to use super() or you're recursing, and __new__ is actually a static method that you have to pass the class to, not a class method, as the actual class hasn't been created yet when it is called. All of these things will be true for a metaclass implementation as well.

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()

Decorator Returning A Class

I originally was writing a comment but it was too long, so I'll add this here. Method #4 is better than the other decorator version, but it's more code than needed for a singleton, and it's not as clear what it does.

The main problems stem from the class being its own base class. First, isn't it weird to have a class be a subclass of a nearly identical class with the same name that exists only in its __class__ attribute? This also means that you can't define any methods that call the method of the same name on their base class with super() because they will recurse. This means your class can't customize __new__, and can't derive from any classes that need __init__ called on them.

When to use the singleton pattern

Your use case is one of the better examples of wanting to use a singleton. You say in one of the comments "To me logging has always seemed a natural candidate for Singletons." You're absolutely right.

When people say singletons are bad, the most common reason is they are implicit shared state. While with global variables and top-level module imports are explicit shared state, other objects that are passed around are generally instantiated. This is a good point, with two exceptions.

The first, and one that gets mentioned in various places, is when the singletons are constant. Use of global constants, especially enums, is widely accepted, and considered sane because no matter what, none of the users can mess them up for any other user. This is equally true for a constant singleton.

The second exception, which get mentioned less, is the opposite -- when the singleton is only a data sink, not a data source (directly or indirectly). This is why loggers feel like a "natural" use for singletons. As the various users are not changing the loggers in ways other users will care about, there is not really shared state. This negates the primary argument against the singleton pattern, and makes them a reasonable choice because of their ease of use for the task.

Here is a quote from http://googletesting.blogspot.com/2008/08/root-cause-of-singletons.html:

Now, there is one kind of Singleton which is OK. That is a singleton where all of the reachable objects are immutable. If all objects are immutable than Singleton has no global state, as everything is constant. But it is so easy to turn this kind of singleton into mutable one, it is very slippery slope. Therefore, I am against these Singletons too, not because they are bad, but because it is very easy for them to go bad. (As a side note Java enumeration are just these kind of singletons. As long as you don't put state into your enumeration you are OK, so please don't.)

The other kind of Singletons, which are semi-acceptable are those which don't effect the execution of your code, They have no "side effects". Logging is perfect example. It is loaded with Singletons and global state. It is acceptable (as in it will not hurt you) because your application does not behave any different whether or not a given logger is enabled. The information here flows one way: From your application into the logger. Even thought loggers are global state since no information flows from loggers into your application, loggers are acceptable. You should still inject your logger if you want your test to assert that something is getting logged, but in general Loggers are not harmful despite being full of state.

蓝礼 2024-12-02 14:00:09
class Foo(object):
     pass

some_global_variable = Foo()

模块只导入一次,其他的都是想多了。不要使用单例,也尽量不要使用全局变量。

class Foo(object):
     pass

some_global_variable = Foo()

Modules are imported only once, everything else is overthinking. Don't use singletons and try not to use globals.

好久不见√ 2024-12-02 14:00:09

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

Use a module. It is imported only once. Define some global variables in it - they will be singleton's 'attributes'. Add some functions - the singleton's 'methods'.

痴骨ら 2024-12-02 14:00:09

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

import datetime
file_name=None

def set_file_name(new_file_name: str):
    global file_name
    file_name=new_file_name

def write(message: str):
    global file_name
    if file_name:
        with open(file_name, 'a+') as f:
            f.write("{} {}\n".format(datetime.datetime.now(), message))
    else:
        print("LOG: {}", message)

使用:

import log
log.set_file_name("debug.log")
log.write("System starting")
...

如果您确实必须有一个单例类,那么我会选择:

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

my_singleton = MySingleton()

使用:

from mysingleton import my_singleton
my_singleton.foo()

where mysingleton.py 是定义 MySingleton 的文件名。这是有效的,因为第一次导入文件后,Python 不会重新执行代码。

You probably never need a singleton in Python. Just define all your data and functions in a module and you have a de facto singleton:

import datetime
file_name=None

def set_file_name(new_file_name: str):
    global file_name
    file_name=new_file_name

def write(message: str):
    global file_name
    if file_name:
        with open(file_name, 'a+') as f:
            f.write("{} {}\n".format(datetime.datetime.now(), message))
    else:
        print("LOG: {}", message)

To use:

import log
log.set_file_name("debug.log")
log.write("System starting")
...

If you really absolutely have to have a singleton class then I'd go with:

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

my_singleton = MySingleton()

To use:

from mysingleton import my_singleton
my_singleton.foo()

where mysingleton.py is your filename that MySingleton is defined in. This works because after the first time a file is imported, Python doesn't re-execute the code.

扛起拖把扫天下 2024-12-02 14:00:09

你只需要一个装饰器,具体取决于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+

Implementation

from functools import cache

@cache
class CustomClass(object):
    ...

You just need a decorator, depending on the python version:


Python 3.2+

Implementation

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

Usage

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

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

Output

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

Notice how foo got printed only once


Python 3.9+

Implementation:

from functools import cache

@cache
class CustomClass(object):
    ...
久光 2024-12-02 14:00:09

这是给您的一句俏皮话:

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

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

Here's a one-liner for you:

singleton = lambda c: c()

Here's how you use it:

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

assert wat.get_x() == 1

Your object gets instantiated eagerly. This may or may not be what you want.

呆° 2024-12-02 14:00:09
  • 如果想要拥有同一类的多个实例,但前提是 args 或 kwargs 不同,可以使用第三方 python 包 方便的装饰器(包装饰器)。
  • 前任。
    1. 如果您有一个处理串行通信的类,并且要创建一个想要将串行端口作为参数发送的实例,那么使用传统方法将不起作用
    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
>>>
  • If one wants to have multiple number of instances of the same class, but only if the args or kwargs are different, one can use the third-party python package Handy Decorators (package decorators).
  • Ex.
    1. If you have a class handling serial communication, and to create an instance you want to send the serial port as an argument, then with traditional approach won't work
    2. Using the above mentioned decorators, one can create multiple instances of the class if the args are different.
    3. For same args, the decorator will return the same instance which is already been created.
>>> 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
>>>
半枫 2024-12-02 14:00:09

使用函数属性也很简单

def f():
    if not hasattr(f, 'value'):
        setattr(f, 'value', singletonvalue)
    return f.value

Using a function attribute is also very simple

def f():
    if not hasattr(f, 'value'):
        setattr(f, 'value', singletonvalue)
    return f.value
沉默的熊 2024-12-02 14:00:09

我将推荐一种使用元类的优雅解决方案

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)

输出:

Creating Spam

正如您从输出中看到的,只有一个对象被实例化

I will recommend an elegant solution using metaclasses

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)

Output:

Creating Spam

As you can see from the output, only one object is instantiated

人生百味 2024-12-02 14:00:09

我更喜欢这个解决方案,我发现它非常清晰和直接。
例如,它正在使用双重检查,如果其他线程已经创建了它。
需要考虑的其他事情是确保反序列化不会创建任何其他实例。
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))

I prefer this solution which I found very clear and straightforward.
It is using double check for instance, if some other thread already created it.
Additional thing to consider is to make sure that deserialization isn't creating any other instances.
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))
最佳男配角 2024-12-02 14:00:09

一种线性解决方案:

将其添加到任何 init self.__class__.__new__ = lambda _: self

class WhateverClass():
    def __init__(self):
        self.__class__.__new__ = lambda _: self

优点

  • 极其简单和简洁。只需在 __init__ 中添加一行
  • 真正的类,不需要模块
  • Pythonic 使用 lambda、闭包和猴子修补

它是如何工作的? __new__ 在第一次调用 __init__ 后被覆盖,因此构造函数本质上是一次性的。 lambda 用于使用闭包返回对 self 的引用,这使得一切都非常匿名。

One liner solution:

Add this to any init self.__class__.__new__ = lambda _: self

class WhateverClass():
    def __init__(self):
        self.__class__.__new__ = lambda _: self

Pros

  • Extremely simple and concise. Just add one line to __init__
  • True class, no modules needed
  • Pythonic use of lambda, closure, and monkey patching

How does it work? __new__ is overwritten after the first call to __init__ so the constructor is essentially single-use. The lambda is used to return a reference to self using a closure which makes everything very anonymous.

蓦然回首 2024-12-02 14:00:09

这是我自己的单例实现。你所要做的就是装饰班级;要获取单例,您必须使用 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

这是代码:

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)

Here's my own implementation of singletons. All you have to do is decorate the class; to get the singleton, you then have to use the Instance method. Here's an example:

@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

And here's the code:

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)
故事还在继续 2024-12-02 14:00:09

使用类变量(无装饰器)

通过重写 __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

Use a class variable (no decorator)

By overriding the __new__ method to return the same instance of the class. A boolean to only initialize the class for the first time:

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
清眉祭 2024-12-02 14:00:09
from functools import cache

@cache
class xxx:
   ....

简单又有效!

from functools import cache

@cache
class xxx:
   ....

Dead easy and works!

悲欢浪云 2024-12-02 14:00:09

方法 3 似乎非常简洁,但是如果您希望程序在 Python 2 中运行Python 3,它不起作用。即使使用 Python 版本的测试来保护单独的变体也会失败,因为 Python 3 版本在 Python 2 中给出了语法错误。

感谢迈克·沃特金斯: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

我认为作业中的“对象”需要替换为“基类”,但我还没有尝试过(我已经尝试过如图所示的代码)。

Method 3 seems to be very neat, but if you want your program to run in both Python 2 and Python 3, it doesn't work. Even protecting the separate variants with tests for the Python version fails, because the Python 3 version gives a syntax error in Python 2.

Thanks to Mike Watkins: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/. If you want the program to work in both Python 2 and Python 3, you need to do something like:

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

I presume that 'object' in the assignment needs to be replaced with the 'BaseClass', but I haven't tried that (I have tried code as illustrated).

前事休说 2024-12-02 14:00:09

我会把我的扔进戒指里。这是一个简单的装饰器。

from abc import ABC

def singleton(real_cls):

    class SingletonFactory(ABC):

        instance = None

        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = real_cls(*args, **kwargs)
            return cls.instance

    SingletonFactory.register(real_cls)
    return SingletonFactory

# Usage
@singleton
class YourClass:
    ...  # Your normal implementation, no special requirements.

我认为它相对于其他一些解决方案的优点是:

  • 它清晰简洁(在我看来;D)。
  • 它的动作是完全封装的。您无需对 YourClass 的实现进行任何更改。这包括不需要为您的类使用元类(请注意,上面的元类位于工厂中,而不是“真正的”类)。
  • 它不依赖于猴子修补任何东西。
  • 它对调用者来说是透明的:
    • 调用者仍然简单地导入 YourClass,它看起来像一个类(因为它确实是),并且他们正常使用它。无需使调用者适应工厂函数。
    • YourClass() 实例化的仍然是您实现的 YourClass 的真实实例,而不是任何类型的代理,因此不会产生副作用。
    • isinstance(instance, YourClass) 和类似的操作仍然按预期工作(尽管这部分确实需要 abc,因此排除了 Python <2.6)。

我确实遇到了一个缺点:真实类的类方法和静态方法不能通过隐藏它的工厂类透明地调用。我很少使用这个,所以我从来没有遇到过这种需要,但是通过在工厂上使用自定义元类来实现 __getattr__() 来委托 all-ish ,可以很容易地纠正它访问真实类的属性。

我实际上发现更有用的一个相关模式(并不是说这些东西经常需要)是“唯一”模式,其中使用相同的参数结果实例化类返回同一个实例。即“每个参数单例”。上面的内容很好地适应了这一点,并且变得更加简洁:

def unique(real_cls):

    class UniqueFactory(ABC):

        @functools.lru_cache(None)  # Handy for 3.2+, but use any memoization decorator you like
        def __new__(cls, *args, **kwargs):
            return real_cls(*args, **kwargs)

    UniqueFactory.register(real_cls)
    return UniqueFactory

话虽如此,我确实同意一般建议,即如果您认为您需要其中一件东西,您真的应该停下来问问自己是否真的需要。 99%的时间都是亚格尼。

I'll toss mine into the ring. It's a simple decorator.

from abc import ABC

def singleton(real_cls):

    class SingletonFactory(ABC):

        instance = None

        def __new__(cls, *args, **kwargs):
            if not cls.instance:
                cls.instance = real_cls(*args, **kwargs)
            return cls.instance

    SingletonFactory.register(real_cls)
    return SingletonFactory

# Usage
@singleton
class YourClass:
    ...  # Your normal implementation, no special requirements.

Benefits I think it has over some of the other solutions:

  • It's clear and concise (to my eye ;D).
  • Its action is completely encapsulated. You don't need to change a single thing about the implementation of YourClass. This includes not needing to use a metaclass for your class (note that the metaclass above is on the factory, not the "real" class).
  • It doesn't rely on monkey-patching anything.
  • It's transparent to callers:
    • Callers still simply import YourClass, it looks like a class (because it is), and they use it normally. No need to adapt callers to a factory function.
    • What YourClass() instantiates is still a true instance of the YourClass you implemented, not a proxy of any kind, so no chance of side effects resulting from that.
    • isinstance(instance, YourClass) and similar operations still work as expected (though this bit does require abc so precludes Python <2.6).

One downside does occur to me: classmethods and staticmethods of the real class are not transparently callable via the factory class hiding it. I've used this rarely enough that I've never happen to run into that need, but it would be easily rectified by using a custom metaclass on the factory that implements __getattr__() to delegate all-ish attribute access to the real class.

A related pattern I've actually found more useful (not that I'm saying these kinds of things are required very often at all) is a "Unique" pattern where instantiating the class with the same arguments results in getting back the same instance. I.e. a "singleton per arguments". The above adapts to this well and becomes even more concise:

def unique(real_cls):

    class UniqueFactory(ABC):

        @functools.lru_cache(None)  # Handy for 3.2+, but use any memoization decorator you like
        def __new__(cls, *args, **kwargs):
            return real_cls(*args, **kwargs)

    UniqueFactory.register(real_cls)
    return UniqueFactory

All that said, I do agree with the general advice that if you think you need one of these things, you really should probably stop for a moment and ask yourself if you really do. 99% of the time, YAGNI.

毅然前行 2024-12-02 14:00:09

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

  1. 使 Foo 的实例可在任何地方(全局)访问。
  2. 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!

Maybe I missunderstand the singleton pattern but my solution is this simple and pragmatic (pythonic?). This code fullfills two goals

  1. Make the instance of Foo accessiable everywhere (global).
  2. Only one instance of Foo can exist.

This is the code.

#!/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()

Output

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!
说不完的你爱 2024-12-02 14:00:09

好吧,除了同意Pythonic关于模块级全局的一般建议之外,这样怎么样:

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *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(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

输出是:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance

Well, other than agreeing with the general Pythonic suggestion on having module-level global, how about this:

def singleton(class_):
    class class_w(class_):
        _instance = None
        def __new__(class2, *args, **kwargs):
            if class_w._instance is None:
                class_w._instance = super(class_w, class2).__new__(class2, *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(object):
    def __init__(self, text):
        print text
    @classmethod
    def name(class_):
        print class_.__name__

x = MyClass(111)
x.name()
y = MyClass(222)
print id(x) == id(y)

Output is:

111     # the __init__ is called only on the 1st time
MyClass # the __name__ is preserved
True    # this is actually the same instance
旧情勿念 2024-12-02 14:00:09

怎么样:

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() 从类“创建”实例(实际上获取唯一的实例)。它还会阻止您通过执行 type(MySingleton)() 来创建其他实例(也返回相同的实例)。

How about this:

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

Use it as a decorator on a class that should be a singleton. Like this:

@singleton
class MySingleton:
    #....

This is similar to the singleton = lambda c: c() decorator in another answer. Like the other solution, the only instance has name of the class (MySingleton). However, with this solution you can still "create" instances (actually get the only instance) from the class, by doing MySingleton(). It also prevents you from creating additional instances by doing type(MySingleton)() (that also returns the same instance).

墟烟 2024-12-02 14:00:09

这个答案可能不是您想要的。我想要一个单例,因为只有该对象才有其身份,以便进行比较。就我而言,它被用作 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

This answer is likely not what you're looking for. I wanted a singleton in the sense that only that object had its identity, for comparison to. In my case it was being used as a Sentinel Value. To which the answer is very simple, make any object mything = object() and by python's nature, only that thing will have its identity.

#!python
MyNone = object()  # The singleton

for item in my_list:
    if item is MyNone:  # An Example identity comparison
        raise StopIteration
胡大本事 2024-12-02 14:00:09

优点

这是一个真正的类自动神奇地覆盖继承使用元类
出于其正当目的(并让我意识到这一点) 缺点

有吗?

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

Pros

It's a true class Auto-magically covers inheritance Uses metaclass
for its proper purpose (and made me aware of it) Cons

Are there any?

This will be problem with serialziation. If you try to deserialize object from file (pickle) it will not use __call__ so it will create new file, you can use base class inheritance with __new__ to prevent that.

一个人的旅程 2024-12-02 14:00:09

如果您想使用实例作为属性,您可以使用元类。例如;

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

You can use a metaclass if you want to use instance as a property. For example;

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
就像说晚安 2024-12-02 14:00:09

我也更喜欢装饰器语法而不是从元类派生。我的两分钱:

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 失败)
  • isinstance(MyClass(), MyClass) 仍然有效(从子句返回函数而不是类 >property、classmethodstaticmethod 仍将按预期工作
  • __init__() 构造函数仅执行一次
  • 您可以从装饰的继承班级(没用?)再次使用@singleton

缺点:

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

如果您需要基于构造函数参数的不同实例,则需要改进此解决方案(siddhesh-suhas-sathe<提供的解决方案/a> 提供了这个)。

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

I also prefer decorator syntax to deriving from metaclass. My two cents:

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(...):
    ...

This has some benefits above other decorators provided:

  • isinstance(MyClass(), MyClass) will still work (returning a function from the clausure instead of a class will make isinstance to fail)
  • property, classmethod and staticmethod will still work as expected
  • __init__() constructor is executed only once
  • You can inherit from your decorated class (useless?) using @singleton again

Cons:

  • print(MyClass().__class__.__name__) will return Singleton instead of MyClass. If you still need this, I recommend using a metaclass as suggested above.

If you need a different instance based on constructor parameters this solution needs to be improved (solution provided by siddhesh-suhas-sathe provides this).

Finally, as other suggested, consider using a module in python. Modules are objects. You can even pass them in variables and inject them in other classes.

一页 2024-12-02 14:00:09

我只是偶然做了一个简单的,我想我会分享它......

class MySingleton(object):
    def __init__(self, *, props={}):
        self.__dict__ = props

mything = MySingleton()
mything.test = 1
mything2 = MySingleton()
print(mything2.test)
mything2.test = 5
print(mything.test)

I just made a simple one by accident and thought I'd share it...

class MySingleton(object):
    def __init__(self, *, props={}):
        self.__dict__ = props

mything = MySingleton()
mything.test = 1
mything2 = MySingleton()
print(mything2.test)
mything2.test = 5
print(mything.test)
丑丑阿 2024-12-02 14:00:09

与 fab 的答案略有相似,但不完全相同。

单例模式不要求我们能够多次调用构造函数次。由于单例应该被创建一次并且只能创建一次,难道它不应该被视为只被创建一次吗? “欺骗”构造函数可能会损害可读性。

所以我的建议就是这样:

class Elvis():
    def __init__(self):
        if hasattr(self.__class__, 'instance'):
            raise Exception()
        self.__class__.instance = self
        # initialisation code...

    @staticmethod
    def the():
        if hasattr(Elvis, 'instance'):
            return Elvis.instance
        return Elvis()

这并不排除用户代码使用构造函数或字段 instance

if Elvis() is King.instance:

...如果您确定 Elvis 有尚未创建,而 King 已*。

但它鼓励用户普遍使用the方法:

Elvis.the().leave(Building.the())

......特别是(尽管不是唯一)如果对猫王和/或<的预先存在存在疑问那个建筑物。

要完成此操作,您还可以覆盖 __delattr__() 以在尝试删除 instance 时引发异常,并覆盖 __del__()这样它就会引发异常(除非我们知道程序正在结束...)


* 这里有一些本体论问题:一个单身人士是否可以成为另一个单身人士?对单例类进行子类化似乎充满了问题。即便如此,您可能会问 isinstance(),而不是 is


进一步改进


我感谢那些提供评论和编辑帮助的人,欢迎提供更多帮助。当我使用 Jython 时,这应该可以更普遍地工作,并且是线程安全的。

try:
    # This is jython-specific
    from synchronize import make_synchronized
except ImportError:
    # This should work across different python implementations
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()
    
        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func

class Elvis(object): # NB must be subclass of object to use __new__
    instance = None

    @classmethod
    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is not None:
            raise Exception()
        cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance
    
    def __init__(self):
        pass
        # initialisation code...

    @classmethod
    @make_synchronized
    def the(cls):
        if cls.instance is not None:
            return cls.instance
        return cls()

注意点:

  1. 如果你没有从 python2.x 中的 object 进行子类化,你将得到一个旧式的类,它不使用 __new__
  2. 装饰 __new__ 时,你必须装饰与@classmethod或__new__将是一个未绑定的实例方法
  3. 这可以通过使用元类来改进,因为这将允许您使the成为类级别属性,可能重命名它到实例

It is slightly similar to the answer by fab but not exactly the same.

The singleton pattern does not require that we be able to call the constructor multiple times. As a singleton should be created once and once only, shouldn't it be seen to be created just once? "Spoofing" the constructor arguably impairs legibility.

So my suggestion is just this:

class Elvis():
    def __init__(self):
        if hasattr(self.__class__, 'instance'):
            raise Exception()
        self.__class__.instance = self
        # initialisation code...

    @staticmethod
    def the():
        if hasattr(Elvis, 'instance'):
            return Elvis.instance
        return Elvis()

This does not rule out the use of the constructor or the field instance by user code:

if Elvis() is King.instance:

... if you know for sure that Elvis has not yet been created, and that King has*.

But it encourages users to use the the method universally:

Elvis.the().leave(Building.the())

... particularly (though not exclusively) if there is doubt concerning the pre-existence of Elvis and/or of that building.

To make this complete you could also override __delattr__() to raise an Exception if an attempt is made to delete instance, and override __del__() so that it raises an Exception (unless we know the program is ending...)


* some ontological issues here: could one singleton ever be another singleton? Subclassing a singleton class seems fraught with problems. And even then you'd probably be asking isinstance(), not is.


Further improvements


My thanks to those who have helped with comments and edits, of which more are welcome. While I use Jython, this should work more generally, and be thread-safe.

try:
    # This is jython-specific
    from synchronize import make_synchronized
except ImportError:
    # This should work across different python implementations
    def make_synchronized(func):
        import threading
        func.__lock__ = threading.Lock()
    
        def synced_func(*args, **kws):
            with func.__lock__:
                return func(*args, **kws)

        return synced_func

class Elvis(object): # NB must be subclass of object to use __new__
    instance = None

    @classmethod
    @make_synchronized
    def __new__(cls, *args, **kwargs):
        if cls.instance is not None:
            raise Exception()
        cls.instance = object.__new__(cls, *args, **kwargs)
        return cls.instance
    
    def __init__(self):
        pass
        # initialisation code...

    @classmethod
    @make_synchronized
    def the(cls):
        if cls.instance is not None:
            return cls.instance
        return cls()

Points of note:

  1. If you don't subclass from object in python2.x you will get an old-style class, which does not use __new__
  2. When decorating __new__ you must decorate with @classmethod or __new__ will be an unbound instance method
  3. This could possibly be improved by way of use of a metaclass, as this would allow you to make the a class-level property, possibly renaming it to instance
瑕疵 2024-12-02 14:00:09

我不记得在哪里找到这个解决方案,但从我的非 Python 专家的角度来看,我发现它是最“优雅”的:

class SomeSingleton(dict):
    __instance__ = None
    def __new__(cls, *args,**kwargs):
        if SomeSingleton.__instance__ is None:
            SomeSingleton.__instance__ = dict.__new__(cls)
        return SomeSingleton.__instance__

    def __init__(self):
        pass

    def some_func(self,arg):
        pass

为什么我喜欢这个?没有装饰器,没有元类,没有多重继承......如果您决定不再希望它成为单例,只需删除 __new__ 方法即可。由于我是 Python(以及一般的 OOP)新手,我希望有人能告诉我为什么这是一个糟糕的方法?

I can't remember where I found this solution, but I find it to be the most 'elegant' from my non-Python-expert point of view:

class SomeSingleton(dict):
    __instance__ = None
    def __new__(cls, *args,**kwargs):
        if SomeSingleton.__instance__ is None:
            SomeSingleton.__instance__ = dict.__new__(cls)
        return SomeSingleton.__instance__

    def __init__(self):
        pass

    def some_func(self,arg):
        pass

Why do I like this? No decorators, no meta classes, no multiple inheritance...and if you decide you don't want it to be a Singleton anymore, just delete the __new__ method. As I am new to Python (and OOP in general) I expect someone will set me straight about why this is a terrible approach?

双手揣兜 2024-12-02 14:00:09

代码基于 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)

说明:

  1. 创建新类,继承给定的cls
    (它不会修改 cls,以防有人想要 singleton(list)

  2. 创建实例。在覆盖 __new__ 之前,这很简单。

  3. 现在,当我们轻松创建实例时,使用刚才定义的方法覆盖 __new__ 。
  4. 该函数仅在调用者期望的情况下返回实例,否则引发TypeError
    当有人尝试从修饰类继承时,条件不满足。

  5. <块引用>

    如果__new__()返回cls的实例,则将调用新实例的__init__()方法 strong> 类似于 __init__(self[, ...]),其中 self 是新实例,其余参数与传递给 __new__() 的参数相同。< /p>

    instance 已初始化,因此函数将 __init__ 替换为不执行任何操作的函数。

查看它在线运行

Code based on Tolli's answer.

#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)

Explanation:

  1. Create new class, inheriting from given cls
    (it doesn't modify cls in case someone wants for example singleton(list))

  2. Create instance. Before overriding __new__ it's so easy.

  3. Now, when we have easily created instance, overrides __new__ using method defined moment ago.
  4. The function returns instance only when it's what the caller expects, otherwise raises TypeError.
    The condition is not met when someone attempts to inherit from decorated class.

  5. If __new__() returns an instance of cls, then the new instance’s __init__() method will be invoked like __init__(self[, ...]), where self is the new instance and the remaining arguments are the same as were passed to __new__().

    instance is already initialized, so function replaces __init__ with function doing nothing.

See it working online

夜深人未静 2024-12-02 14:00:09

如果您不需要 Singleton 实例的延迟初始化,那么以下操作应该很简单且线程安全:

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

这样 A 是在模块导入时初始化的单例。

If you don't need lazy initialization of the instance of the Singleton, then the following should be easy and thread-safe:

class A:
    instance = None
    # Methods and variables of the class/object A follow
A.instance = A()

This way A is a singleton initialized at module import.

歌入人心 2024-12-02 14:00:09

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

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>...

After struggling with this for some time I eventually came up with the following, so that the config object would only be loaded once, when called up from separate modules. The metaclass allows a global class instance to be stored in the builtins dict, which at present appears to be the neatest way of storing a proper program global.

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>...
别在捏我脸啦 2024-12-02 14:00:09

一条衬垫(我并不自豪,但它确实有效):

import sys

class Myclass:
  def __init__(self):
     # do your stuff
      vars(sys.modules[__name__])[type(self).__name__] = lambda: self # singletonify

One liner (I am not proud, but it does the job):

import sys

class Myclass:
  def __init__(self):
     # do your stuff
      vars(sys.modules[__name__])[type(self).__name__] = lambda: self # singletonify
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文