python单例应用场景 - python单例装饰器




在Python中创建一个单例 (13)

使用Metaclass

我会推荐方法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的(唯一)实例的类型为class 'your_module.Logger' 。 当你使用Logger()调用logger时,Python首先会询问LoggerSingleton的元类,该怎么做,从而允许实例创建被预先占用。 这个过程与Python通过调用__getattr__在通过执行myclass.attribute引用其中一个属性时要求某个类执行什么操作相同。

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

在这种情况下,如果使用Method#2 ,并且子类定义了__new__方法,则每次调用SubClassOfSingleton() 都会执行它 - 因为它负责调用返回存储实例的方法。 使用元类时,只会在创建唯一实例时调用它。 你想定制调用该类的意思 ,这是由它的类型决定的。

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

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

你也会听说单身模式违反了“单一责任原则” - 每个班级应做一件事 。 这样,如果您需要更换另一个代码,您不必担心代码会搞乱一件事,因为它们是分开的和封装的。 元类实现通过了这个测试 。 元类负责强制执行模式 ,创建的类和子类不需要知道它们是单例方法#1未通过此测试,正如您在“MyClass本身是一个函数,而不是一个类,因此您无法从中调用类方法”中所记录的。

Python 2和3兼容版本

编写在Python2和Python3中都可用的东西需要使用稍微复杂的方案。 由于元类通常是类型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

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

更正

在另一个主题上,您可能已经注意到了这一点,但原始帖子中的基类实现是错误的。 _instances需要在类中引用 ,你需要使用super()或者你正在__new__ ,而__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没有全局状态,因为所有对象都是常量。 但把这种单身人士变成易变的单身人士是很容易的,这是非常滑的斜坡。 因此,我也反对这些单身人士,不是因为他们不好,而是因为他们很容易变坏。 (作为一个便笺,Java枚举就是这种单例,只要你没有把枚举放进你的枚举中,你就可以,所以请不要。)

其他类型的Singletons是半可接受的,它们不影响代码的执行,它们没有“副作用”。 记录就是一个很好的例子。 它加载了单例和全局状态。 这是可以接受的(因为它不会伤害到你),因为不管给定的记录器是否启用,你的应用程序都不会有任何不同。 这里的信息以一种方式流动:从您的应用程序到记录器。 即使记录器是全局状态,因为没有信息从记录器流入您的应用程序,记录器是可以接受的。 如果你想让你的测试断言某些东西被记录下来,你仍然应该注入你的记录器,但是一般情况下,尽管记录器处于满状态,但它并不是有害的。

这个问题不是用来讨论单例设计模式是否合意,是反模式还是用于任何宗教战争,而是讨论如何在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

优点

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

缺点

  • 尽管使用MyClass()创建的对象将是真正的单例对象,但MyClass本身是一个函数,而不是一个类,因此您无法从中调用类方法。 另外对于m = MyClass(); n = MyClass(); o = type(n)(); 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

优点

  • 这是一个真正的阶级

缺点

  • 多重继承 - eugh! 在继承第二个基类时, __new__可能被覆盖? 人们必须思考超过必要的。

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

优点

  • 这是一个真正的阶级
  • 自动奇迹般地覆盖继承
  • 为了正确的目的而使用__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__的类__new__子类。

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


This is my preferred way of implementing singletons:

class Test(object):
    obj = None

    def __init__(self):
        if Test.obj is not None:
            raise Exception('A Test Singleton instance already exists')
        # Initialization code here

    @classmethod
    def get_instance(cls):
        if cls.obj is None:
            cls.obj = Test()
        return cls.obj

    @classmethod
    def custom_method(cls):
        obj = cls.get_instance()
        # Custom Code here

This solution causes some namespace pollution at the module level (three definitions rather than just one), but I find it easy to follow.

I'd like to be able to write something like this (lazy initialization), but unfortunately classes are not available in the body of their own definitions.

# wouldn't it be nice if we could do this?
class Foo(object):
    instance = None

    def __new__(cls):
        if cls.instance is None:
            cls.instance = object()
            cls.instance.__class__ = Foo
        return cls.instance

Since that isn't possible, we can break out the initialization and the static instance in

Eager Initialization:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        return foo_instance


foo_instance = FooMaker()
foo_instance.__class__ = Foo

Lazy initialization:

Eager Initialization:

import random


class FooMaker(object):
    def __init__(self, *args):
        self._count = random.random()
        self._args = args


class Foo(object):
    def __new__(self):
        global foo_instance
        if foo_instance is None:
            foo_instance = FooMaker()
        return foo_instance


foo_instance = None

代码基于托利的答案 。

#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. 该函数仅在调用者期望的instance返回instance ,否则引发TypeError
    当有人试图从装饰类继承时,条件不满足。

  5. 如果__new__()返回一个cls实例,则新实例的__init__()方法将__init__(self[, ...])一样被调用 ,其中self是新实例,其余参数与传递给__new__()

    instance已经被初始化,所以函数将函数替换为__init__ ,而函数什么也不做。

看到它在线工作


以下是您的一行代码:

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

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


使用一个模块。 它只进口一次。 定义一些全局变量 - 它们将是单例的“属性”。 添加一些功能 - 单身人士的“方法”。


我不记得我在哪里找到这个解决方案,但是从我的非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),我希望有人会让我直言不讳,为什么这是一种可怕的方法?




这个怎么样:

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()从该类“创建”实例(实际上只获得唯一的实例MySingleton() 。 它还阻止你通过type(MySingleton)()创建额外的实例(也返回相同的实例)。


这是我自己实现的单身人士。 你所要做的就是装饰课堂; 要获得单身人士,则必须使用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)

class Foo(object):
     pass

some_global_variable = Foo()

模块只能导入一次,其他所有内容都会过时。 不要使用单例,并尽量不要使用全局变量。





metaclass