在Python中创建单例 [英] Creating a singleton in Python

查看:84
本文介绍了在Python中创建单例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题不是用于讨论单个设计模式的问题最好是将其作为反模式,或用于任何宗教战争,但要讨论如何在Python中以最有效的方式最好地实现此模式.在这种情况下,我将最pythonic"定义为意味着它遵循最少惊讶的原理"..

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

我有多个将成为单例的类(我的用例用于记录器,但这并不重要).当我可以简单地继承或修饰时,我不希望增加gumph来使几个类杂乱无章.

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.

最佳方法:

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本身是一个函数,而不是一个类,因此您不能从中调用类方法.同样对于m = MyClass(); n = MyClass(); o = type(n)();然后是m == n && m != o && n != o
  • While objects created using MyClass() would be true singleton objects, MyClass itself is a a function, not a class, so you cannot call class methods from it. Also for m = MyClass(); n = MyClass(); o = type(n)(); then m == n && m != o && n != o
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__?人们必须思考的事情超出了必要.
  • Multiple inheritance - eugh! __new__ could be overwritten during inheritance from a second base class? One has to think more than is necessary.
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__(并使我意识到)
  • It's a true class
  • Auto-magically covers inheritance
  • Uses __metaclass__ for its proper purpose (and made me aware of it)

缺点

  • 有没有?
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__的类子类化.
  • 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__.

模块文件singleton.py

优点

  • 简单胜于复杂

缺点

推荐答案

使用元类

我建议使用方法2 ,但是最好使用元类,而不是基类.这是一个示例实现:

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

或者在Python3中

Or in Python3

class Logger(metaclass=Singleton):
    pass

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

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

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

Singleton.__call__中的if语句.

关于元类的几句话.元类是类的;也就是说,类是其元类的实例.您可以使用type(obj)在Python中找到对象的元类.普通的新样式类的类型为type.上面代码中的Logger将是class 'your_module.Singleton'类型,就像Logger的(唯一)实例将是class 'your_module.Logger'类型一样.当您使用Logger()调用logger时,Python首先询问LoggerSingleton的元类,该怎么做,从而可以抢先创建实例.此过程与Python通过执行myclass.attribute引用类的一个属性时,调用类调用Python询问__getattr__一样.

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 it's attributes by doing myclass.attribute.

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

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.

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

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 it's type.

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

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

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

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.

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

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

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

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

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

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.

在另一个主题上,您可能已经注意到了这一点,但是原始文章中的基类实现是错误的. _instances需要在类中被引用,您需要使用super()递归,并且__new__实际上是您拥有的静态方法将该类传递给,而不是使用类方法,因为尚未创建实际的类 .所有这些对于元类实现也是正确的.

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

装饰器返回类

我本来是在写评论,但评论太长了,因此我将在此处添加. 方法#4 比其他装饰器版本要好,但是它的代码比单例所需的代码多,并且它的作用还不清楚.

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.

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

The main problems stem from the class being it's 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.

您的用例是想要使用单例的更好的例子之一.您在其中一项评论中说:对我而言,伐木一直是Singletons的自然选择."您绝对正确.

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.

这是来自 http://googletesting.blogspot的引用.com/2008/08/root-cause-of-singletons.html :

现在,有一种Singleton可以.那是所有可达对象都是不可变的单例.如果所有对象都是不可变的,则Singleton没有全局状态,因为一切都是恒定的.但是将这种单身人士变成易变的人是如此容易,这是很滑的坡度.因此,我也反对这些Singleton,不是因为它们不好,而是因为它们很容易变坏. (作为附带说明,Java枚举就是这些单例.只要您不将状态放入枚举中就可以,所以请不要这样做.)

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

另一种半可接受的单例是不影响代码执行的单例,它们没有副作用".日志记录就是一个很好的例子.它加载了Singletons和全局状态.这是可以接受的(因为它不会对您造成伤害),因为无论是否启用给定的记录器,您的应用程序的行为都没有任何不同.此处的信息以一种方式流动:从您的应用程序进入记录器.甚至认为记录器是全局状态,因为没有信息从记录器流到您的应用程序中,所以记录器是可以接受的.如果您想让测试断言某些东西正在被记录,那么您仍然应该注入记录器,但是总的来说,记录器即使处于满状态也不会有害.

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.

这篇关于在Python中创建单例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆