代码之家  ›  专栏  ›  技术社区  ›  ajthinking

非模特辛格尔顿与django?[副本]

  •  0
  • ajthinking  · 技术社区  · 4 年前

    这个问题不适合讨论 singleton design pattern 是可取的,是一种反模式,或者适用于任何宗教战争,但要讨论如何以最具Python风格的方式在Python中最好地实现这种模式。在这个例子中,我将“最蟒蛇”定义为遵循“最少惊讶原则” 。

    我有多个类,它们将成为singleton(我的用例是用于记录器的,但这并不重要)。当我可以简单地继承或装饰时,我不希望用添加的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
    

    优点

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

    Cons

    • 使用创建对象时 MyClass() 将是真正的单例对象, MyClass 它本身是一个函数,而不是一个类,所以不能从中调用类方法

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

    然后 x == y 但是 x != 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
    

    优点

    • 这是一门真正的课

    Cons

    • 多重继承-啊! __new__ 在从第二个基类继承的过程中会被覆盖吗?一个人必须想得比必要的还多。

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

    优点

    • 这是一门真正的课
    • Auto神奇地覆盖了继承
    • 用途 __metaclass__ 为了它的正当目的(并让我意识到这一点)

    Cons

    • 有吗?

    方法4:decorator返回具有相同名称的类

    def singleton(class_):
        class class_w(class_):
            _instance = None
            def __new__(class_, *args, **kwargs):
                if class_w._instance is None:
                    class_w._instance = super(class_w,
                                        class_).__new__(class_,
                                                        *args,
                                                        **kwargs)
                    class_w._instance._sealed = False
                return class_w._instance
            def __init__(self, *args, **kwargs):
                if self._sealed:
                    return
                super(class_w, self).__init__(*args, **kwargs)
                self._sealed = True
        class_w.__name__ = class_.__name__
        return class_w
    
    @singleton
    class MyClass(BaseClass):
        pass
    

    优点

    • 这是一门真正的课
    • Auto神奇地覆盖了继承

    Cons

    • 创建每个新类是否都有开销?在这里,我们为每个类创建两个类,我们希望使其成为单例。虽然这对我来说很好,但我担心这可能不会扩大规模。当然,扩大这种模式是否太容易,还有一个争论的问题。。。
    • _sealed 属性
    • 不能使用在基类上调用相同名称的方法 super() 因为它们会重复出现。这意味着您无法自定义 __新的__ 并且不能对需要调用的类进行子类化 __init__ 。

    方法5:模块

    模块文件 singleton.py

    优点

    • 简单胜于复杂

    Cons

    • 未延迟实例化
    0 回复  |  直到 4 年前
        1
  •  983
  •   Community Mohan Dere    6 年前

    使用元类

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

    或者在Python中3

    class Logger(metaclass=Singleton):
        pass
    

    如果你想跑步 __init__ 每次调用类时,添加

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

    到 if 中的语句 Singleton.__call__ 。

    关于元类的几句话。元类是 阶级中的阶级 ;也就是说,类是 其元类的实例 。您可以在Python中找到对象的元类 type(obj) .普通的新式班 type 。 Logger 在上面的代码中将是类型 class 'your_module.Singleton' ,作为的(唯一)实例 Logger 将是类型 class 'your_module.Logger' .当您使用调用记录器时 Logger() ,Python首先询问的元类 Logger , Singleton ,该怎么做,允许实例创建被预先占用。这个过程与Python通过调用 __getattr__ 当您通过执行 myclass.attribute 。

    元类本质上决定 类的定义是什么意思 以及如何实现这一定义。请参见示例 http://code.activestate.com/recipes/498149/ ,本质上再现了C风格 struct s在Python中使用元类。螺纹 What are some (concrete) use-cases for metaclasses? 还提供了一些示例,它们通常似乎与声明性编程有关,尤其是在ORM中使用的。

    在这种情况下,如果您使用 方法#2 ,并且子类定义 __new__ 方法,它将 每次执行 你打电话 SubClassOfSingleton() --因为它负责调用返回存储实例的方法。有了元类,它将 只能调用一次 ,当创建唯一实例时。你想 自定义调用类的含义 ,这取决于它的类型。

    一般来说 有道理 使用元类来实现单例。单身汉之所以特别,是因为 仅创建一次 ,元类是您自定义 创建一个类 .使用元类可以 更多的控制 以防您需要以其他方式自定义singleton类定义。

    你的单身汉 不需要多重继承 (因为元类不是基类),但是 创建的类的子类 如果使用多重继承,则需要确保singleton类是 第一个/最左边 一个具有重新定义的元类 __call__ 这不太可能成为一个问题。实例dict是 不在实例的命名空间中 这样它就不会意外地覆盖它。

    您还会听到singleton模式违反了“单一责任原则”——每个类都应该这样做 只有一件事 。这样,如果你需要更改另一件代码,你就不必担心会弄乱代码的一件事,因为它们是分离和封装的。元类实现 通过此测试 元类负责 强制执行模式 并且创建的类和子类不需要 意识到他们是单身 。 方法#1 测试失败,正如您所指出的“MyClass本身是一个函数,而不是一个类,所以您不能从中调用类方法。”

    Python 2和3兼容版本

    编写既适用于Python 2又适用于Python 3的东西需要使用稍微复杂一点的方案。由于元类通常是类型的子类 类型 ,可以使用一个在运行时动态创建一个中间基类,并将其作为元类,然后使用 那个 作为公众的基本阶层 辛格尔顿 基类。解释起来难做起来难,如下图所示:

    # 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() 或者你 递归 和 __新的__ 实际上是一个静态方法 把课传给 ,而不是类方法,作为实际的类 尚未创建 然而当它被调用时。所有这些对于元类实现也是正确的。

    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版本更好,但它的代码比单例所需的要多,而且它的作用还不太清楚。

    主要的问题源于这个类是它自己的基类。首先,让一个类成为一个几乎相同的类的子类,并且只存在于它的 __class__ 属性这也意味着你不能定义 在基类上调用同名方法的任何方法 具有 super() 因为它们会重复出现。这意味着您的类无法自定义 __新的__ ,并且不能从任何需要的类派生 __初始化__ 呼吁他们。

    何时使用singleton模式

    您的用例是 一个更好的例子 想要使用singleton。你在一条评论中说:“对我来说,登录似乎一直是单身汉的自然选择。” 绝对正确 。

    当人们说单身汉不好时,最常见的原因是他们 隐式共享状态 。而使用全局变量和顶级模块导入 明确的 共享状态,传递的其他对象通常被实例化。这是一个很好的观点, 除了两个例外 。

    第一个,也是在不同地方提到的一个,是当单身汉 常数 .全局常数的使用,特别是枚举,被广泛接受,并被认为是合理的,因为无论怎样, 任何用户都不能为其他用户搞砸 。对于常量singleton也是如此。

    第二个例外是相反的——当singleton 只有一个数据接收器 ,而不是数据源(直接或间接)。这就是为什么伐木工人觉得单身是一种“自然”的用途。由于不同的用户 不更改记录器 在其他用户关心的方面 不是真正共享的状态 。这否定了反对singleton模式的主要论点,并使它们成为一个合理的选择,因为它们 易用性 任务。

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

    现在,有一种Singleton是可以的。那就是Singleton,其中所有可到达的对象都是不可变的。如果所有对象都是不可变的,那么Singleton就没有全局状态,因为一切都是恒定的。但是,将这种单例转换为可变单例是非常容易的,这是一个非常棘手的问题。因此,我也反对这些单身汉,不是因为他们不好,而是因为他们很容易变坏。(顺便说一句,Java枚举只是这类单例。只要你不把状态放入枚举中,你就可以了,所以请不要。)

    另一种是半可接受的Singleton,它们不会影响代码的执行,它们没有“副作用”。日志记录就是一个完美的例子。它充满了辛格尔顿和全球状态。它是可以接受的(因为它不会伤害你),因为无论是否启用给定的记录器,你的应用程序的行为都没有任何不同。这里的信息以一种方式流动:从应用程序到记录器。即使认为记录器是全局状态,因为没有信息从记录器流到您的应用程序,记录器也是可以接受的。如果你想让你的测试断言某个东西正在被记录,你仍然应该注入你的记录器,但通常情况下,记录器是无害的,尽管它充满了状态。

        2
  •  135
  •   Cat Plus Plus    15 年前
    class Foo(object):
         pass
    
    some_global_variable = Foo()
    

    模块只导入一次,其他的都想得太多了。不要使用singleton,尽量不要使用globals。

        3
  •  86
  •   warvariuc    15 年前

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

        4
  •  61
  •   Neuron MonoThreaded    4 年前

    您可能永远不需要Python中的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)
    

    要使用:

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

    如果你真的必须有一个singleton类,那么我会选择:

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

    要使用:

    from mysingleton import my_singleton
    my_singleton.foo()
    

    哪里 mysingleton.py 你的文件名是 MySingleton 这是因为在第一次导入文件后,Python不会重新执行代码。

        5
  •  13
  •   Jonas Kölker    12 年前

    给你一句话:

    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
    

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

        6
  •  12
  •   Community Mohan Dere    9 年前

    检查堆栈溢出问题 Is there a simple, elegant way to define singletons in Python? 有几种解决方案。

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

        7
  •  9
  •   buhtz    6 年前
    • 如果想要拥有同一类的多个实例,但前提是args或kwargs不同,则可以使用第三方python包 Handy Decorators (包装 decorators )。
    • Ex。
      1. 如果你有一个类处理 serial 通信,并且要创建一个要将串行端口作为参数发送的实例,那么使用传统方法将不起作用
      2. 使用上面提到的decorator,如果参数不同,可以创建类的多个实例。
      3. 对于相同的args,decorator将返回已经创建的相同实例。
    >>> from decorators import singleton
    >>>
    >>> @singleton
    ... class A:
    ...     def __init__(self, *args, **kwargs):
    ...         pass
    ...
    >>>
    >>> a = A(name='Siddhesh')
    >>> b = A(name='Siddhesh', lname='Sathe')
    >>> c = A(name='Siddhesh', lname='Sathe')
    >>> a is b  # has to be different
    False
    >>> b is c  # has to be same
    True
    >>>
    
        8
  •  8
  •   Ruben Decrop    6 年前

    使用函数属性也非常简单

    def f():
        if not hasattr(f, 'value'):
            setattr(f, 'value', singletonvalue)
        return f.value
    
        9
  •  6
  •   Neuron MonoThreaded    4 年前

    这是我自己实现的singleton。你所要做的就是布置班级;要获得singleton,您必须使用 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)
    
        10
  •  5
  •   Andrei R.    5 年前

    我更喜欢这个解决方案,我觉得它非常清晰明了。 例如,如果其他线程已经创建了它,它将使用双重检查。 需要考虑的另一件事是确保反序列化不会创建任何其他实例。 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))
    
        11
  •  4
  •   socketpair    4 年前
    from functools import cache
    
    @cache
    class xxx:
       ....
    

    非常简单而且有效!

        12
  •  4
  •   DEEPAK SURANA    4 年前

    使用类变量(无装饰器)

    通过覆盖 __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
    
        13
  •  3
  •   Peter Mortensen Pieter Jan Bonestroo    10 年前

    方法3看起来非常简洁,但如果您希望程序同时在这两种情况下运行 Python 2 和 Python 3 ,它不起作用。甚至用Python版本的测试来保护单独的变体也失败了,因为Python3版本在Python2中给出了语法错误。

    感谢Mike Watkins: http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ 。如果你想让程序同时在Python2和Python3中运行,你需要做一些事情,比如:

    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
    

    我认为赋值中的“object”需要替换为“BaseClass”,但我还没有尝试过(我已经尝试过如图所示的代码)。

        14
  •  3
  •   mikenerone    10 年前

    我要把我的扔到拳击场上。这是一个简单的装饰师。

    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() instantiates仍然是 YourClass 您实现了,而不是任何类型的代理,因此不可能产生副作用。
      • isinstance(instance, YourClass) 并且类似的操作仍然如预期的那样工作(尽管该位确实需要abc,因此排除了Python<2.6)。

    我确实遇到了一个缺点:真实类的类方法和静态方法不能通过隐藏它的工厂类透明地调用。我很少使用这种方法,从来没有遇到过这种需要,但通过在工厂上使用一个自定义元类可以很容易地纠正它 __getattr__() 将所有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%的时候,YAGNI。

        15
  •  3
  •   Dorcioman    6 年前

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

    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
    

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

        16
  •  2
  •   Guard    15 年前

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

    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
    
        17
  •  2
  •   Tolli    11 年前

    这个怎么样:

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

    将它用作应该是singleton的类的装饰器。像这样:

    @singleton
    class MySingleton:
        #....
    

    这与 singleton = lambda c: c() 装饰师在另一个答案中。与其他解决方案一样,唯一的实例具有类的名称( MySingleton )。然而,使用此解决方案,您仍然可以通过执行 MySingleton() 。它还阻止您通过执行 type(MySingleton)() (也返回相同的实例)。

        18
  •  2
  •   ThorSummoner    10 年前

    这个答案可能不是你想要的。我想要一个singleton,因为只有那个对象才有它的身份,以便与之进行比较 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
    
        19
  •  2
  •   buhtz    7 年前

    也许我误解了singleton模式,但我的解决方案是简单实用的(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!
    
        20
  •  2
  •   Vintage    5 年前

    优点

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

    有吗?

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

        21
  •  2
  •   Boriel    5 年前

    比起从元类派生,我更喜欢decorator语法。我的2美分:

    from typing import Callable, Dict, Set
    
    
    def singleton(cls_: Callable) -> type:
        """ Implements a simple singleton decorator
        """
        class Singleton(cls_):  # type: ignore
            __instances: Dict[type, object] = {}
            __initialized: Set[type] = set()
    
            def __new__(cls, *args, **kwargs):
                if Singleton.__instances.get(cls) is None:
                    Singleton.__instances[cls] = super().__new__(cls, *args, **kwargs)
                return Singleton.__instances[cls]
    
            def __init__(self, *args, **kwargs):
                if self.__class__ not in Singleton.__initialized:
                    Singleton.__initialized.add(self.__class__)
                    super().__init__(*args, **kwargs)
    
        return Singleton
    
    
    @singleton
    class MyClass(...):
        ...
    

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

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

    缺点:

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

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

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

        22
  •  2
  •   byteface    5 年前

    我只是偶然做了一个简单的,想和大家分享一下。。。

    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)
    
        23
  •  2
  •   mike rodent    4 年前

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

    这个 singleton pattern 不要求我们能够多次调用构造函数。作为一个单例应该只创建一次,难道不应该被看作只创建了一次吗?可以说,“欺骗”构造函数会损害易读性。

    所以我的建议是:

    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__() 在尝试删除时引发异常 例子 ,和覆盖 __del__() 从而引发异常(除非我们知道程序正在结束…)

    进一步改进


    我感谢那些在评论和编辑方面提供帮助的人,欢迎更多的评论和编辑。虽然我使用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中的对象派生子类,你会得到一个老式的类,它不使用 __new__
    2. 装饰时 __新的__ 必须使用@classmethod或 __新的__ 将是一个未绑定的实例方法
    3. 这可能会通过使用元类来改进,因为这将允许您 这个 类级属性,可能将其重命名为 例子
        24
  •  1
  •   2cynykyl    11 年前

    我不记得在哪里找到了这个解决方案,但从我的非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)的新手,我希望有人会告诉我为什么这是一种糟糕的方法?

        25
  •  1
  •   Community Mohan Dere    9 年前

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

    说明:

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

    2. 创建实例。覆盖之前 __new__ 这太容易了。

    3. 现在,当我们轻松地创建了实例时,重写 __新的__ 使用刚才定义的方法。
    4. 函数返回 instance 只有当它是调用者所期望的时,否则会引发 TypeError 。
      当有人试图从装饰类继承时,不满足条件。

    5. 如果 __new__() 返回的实例 cls ,然后是新实例 __init__() 方法将被调用 喜欢 __init__(self[, ...]) ,其中self是新实例,其余参数与传递给的参数相同 __新建__() 。

      例子 已初始化,因此函数替换 __init__ 函数什么也不做。

    See it working online

        26
  •  1
  •   Serge Rogatch    7 年前

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

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

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

        27
  •  1
  •   Den-Jason    6 年前

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

    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>...
    
        28
  •  1
  •   omerfarukdogan    5 年前

    您可以使用 metaclass 如果你想使用 instance 作为一种财产。例如

    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
    
        29
  •  1
  •   polvoazul    4 年前

    一句话(我并不自豪,但它确实起到了作用):

    import sys
    
    class Myclass:
      def __init__(self):
         # do your stuff
          vars(sys.modules[__name__])[type(self).__name__] = lambda: self # singletonify
    
        30
  •  1
  •   Markus Hirsimäki    4 年前

    方法:一次性使用后覆盖__new__

    class Singleton():
        def __init__(self):
            Singleton.instance = self
            Singleton.__new__ = lambda _: Singleton.instance
    

    优点

    • 非常简洁
    • 真正的类,不需要模块
    • 正确使用lambda和蟒蛇猴子补丁

    Cons

    • __new__可能再次被覆盖