似乎有很多方法可以在Python中定义单例。是否对Stack Overflow有共识?

评论

单身人士是病态的骗子,不是吗?

“此问题不适合我们的问答格式”-我认为这不是一个主观问题,有没有办法提出这样的问题以使其符合SO问答格式?

我不同意这不是建设性的。如果移至programmers.stackexchange.com,可以重新打开吗?

@stackoverflowwww否,因为它是基于意见的,并且是编程序。SE不喜欢这样。

@ratchetfreak使这个问题流行的是,像我这样的人正在寻找不同的方法来在python中创建Singleton。有一些替代方案各有利弊,或者仅在某些情况下才适用。这个问题可以重新定义为“在Python中创建单例的方式有哪些?我特别感兴趣的是基于类的解决方案和基于类实例的解决方案之间的区别。”

#1 楼

我没有真正的需要,因为具有功能(而不是类)的模块可以很好地用作单例。它的所有变量都将绑定到模块,该模块无论如何都无法重复实例化。

如果您希望使用类,则无法在Python中创建私有类或私有构造函数,因此,您只能通过使用您的约定来保护自己免受多重实例化的侵害。 API。我仍然只是将方法放在模块中,并将模块视为单例。

评论


构造函数不能只是检查实例是否已经创建,如果实例已经抛出,则抛出异常吗?

– Casebash
2010年5月16日上午7:56

只要您不需要在设计中使用继承,就可以了,在这种情况下,以下大多数答案更合适

–吉姆·杰弗里斯(Jim Jeffries)
2011年6月6日上午8:23

循环导入时坏了

–死
2012年9月25日上午10:49

如果我希望该模块可继承该怎么办?

– yossi
2012年11月26日9:09

我认为这是错误的。关于模块级接口的一个烦恼是管理导入。例如,Python日志记录是模块级别的接口。为了确保您在登录后完全清理,必须调用logging.shutdown()。这意味着您必须将日志记录导入到调用shutdown的模块中。如果是单例模式,则可以在实例所传递到的任何模块中对其调用shutdown。

–马特
16-2-25在6:49

#2 楼

这是我自己的单例实现。您要做的就是装饰教室。要获取单例,则必须使用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. Also, the decorated class cannot be
    inherited from. 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.

    """

    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)


评论


Python包含电池,这应该成为desing_pattern标准库的一部分,谢谢

–破破烂烂
14年8月31日,0:23

此装饰器不支持带参数的构造函数。对于更通用的装饰器,请使用:def Instance(self,* args,** kwargs):self._instance = self._decorated(* args,** kwargs)

– akhan
17年4月28日在5:16



@akhan我决定不故意使用参数支持构造函数,因为参数只会在第一次使用,而在其他所有时间都将被忽略。因为您可能在不同的地方使用了不同的参数,但是这可能会使您的代码难以理解,但是您可能不知道这些调用中的哪一个实际上是初始化单例的调用。

– Paul Manta
17年4月28日在7:53

@akhan如果您真的想用参数初始化单例,则应该有一个单独的initialize()方法,该方法可以接受任何参数,如果多次调用,则抛出该异常。

– Paul Manta
17年4月28日在7:54

这是一个非常糟糕的单例实现。首先,它不是合适的装饰器,因为它不使用functools.wraps或functools.update_wrapper。其次,必须通过调用Foo.Instance()来获取实例非常令人讨厌,并且有0个原因不能将其实现为Foo()。第三,替换这样的类会产生意外的结果,例如type(Foo.instance())为Foo-> False

–阿兰·费
18年5月28日在12:59

#3 楼

您可以像这样覆盖__new__方法:

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(
                                cls, *args, **kwargs)
        return cls._instance


if __name__ == '__main__':
    s1 = Singleton()
    s2 = Singleton()
    if (id(s1) == id(s2)):
        print "Same"
    else:
        print "Different"


评论


警告:如果__new __()返回cls的实例,则新实例的__init __()方法将像__init __(self [,...])一样被调用,其中self是新实例,其余参数与传递给__new __()。如果Singleton的任何子类实现__init __(),则将使用相同的self多次调用它。我最终改为使用工厂。

–alsuren
2011-09-8 12:48



使用元类作为此处的答案会更好:stackoverflow.com/a/33201/804147

–欠载
2012年1月23日15:24

这给出以下警告-singleton.py:9:DeprecationWarning:object .__ new __()不带参数cls._instance = super(Singleton,cls).__ new __(cls,* args,** kwargs)

–Siddhant
2013年1月2日在16:17



@Siddhant:更糟糕的是,在Python 3中,警告变成了错误。有关更多详细信息,请参见bugs.python.org/issue1683368和blog.jaraco.com/2014/05/…。

–Jason R. Coombs
2014年6月14日下午16:36

#4 楼

在Python中实现单例的一种稍有不同的方法是Alex Martelli(Google员工和Python天才)的borg模式。

class Borg:
    __shared_state = {}
    def __init__(self):
        self.__dict__ = self.__shared_state


所以不必强迫所有实例具有相同的身份,他们共享状态。

评论


也称为单州。可能比单例更邪恶。

– Tom Hawtin-大头钉
09年11月1日在11:52

不适用于新样式类

–詹姆斯·埃默顿(James Emerton)
2010年6月4日23:54

有谁能解释为什么这不适用于新型类?

–斯蒂芬·埃斯利(Stephen Emslie)
2011年6月9日9:08

@JamesEmerton:我刚刚尝试使用Python 2.7.2,可以在新样式类上正常工作。

–voithos
2012-09-26 17:22



@pylover:没错,这不是Singleton,这可能是Alex Martelli为其改名的部分原因,但其效果却非常相似。

–马丁内
13年4月10日在19:47



#5 楼

模块方法效果很好。如果我绝对需要单身人士,则可以使用元类方法。

class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls.instance = None 

    def __call__(cls,*args,**kw):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kw)
        return cls.instance

class MyClass(object):
    __metaclass__ = Singleton


评论


此模式违反“单一责任原则”(c2.com/cgi/wiki?SingleResponsibilityPrinciple)。请参阅blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx中的第(2)点。

–haridsv
2010年5月20日19:21

@haridsv我不同意。类是单例的事实在元类实现中被抽象化了-类本身不知道或不在乎它是单例,因为它不负责强制执行该要求,元类则是。如您所注意到的,下面的方法显然是违反的。基类方法介于两者之间。

–agf
2011年7月23日下午4:38

这个问题是您可以通过Deepcopy创建MyClass的多个实例。如果从复制运行,则将deepcopy作为dcp导入; m1 = MyClass(); m2 = dcp(m1);打印m1为m2,您将得到False。你知道吗?

–user312650
2012年3月27日在9:29

@ dare2be:仅仅通过让元类还向创建的类添加__deepcopy __()方法,就不能解决您提到的复制问题吗?

–马丁内
2012年12月3日在18:18

@martineau:这是类型。__init__是重载,而不是MyClass。__init__

–埃里克
2014年2月3日0:00,

#6 楼

请参阅PEP318的此实现,并使用装饰器实现单例模式:

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...


评论


这个装饰器的问题是'MyClass'不再是一个类,例如super()不起作用,类方法不起作用,例如:@singleton class MyClass(BaseClass):def __init __(self):super(MyClass,self).__ init __()

–ithkuil
2011-10-28 9:40



似乎装饰器应该将新方法而不是类应用于新方法,以处理继承问题。在这一点上,装饰器的优雅可读性降低了。或者装饰器需要对正在装饰的类进行煎炸,以使新功能执行明智的操作。

– F1谣言
16年6月29日在17:01

#7 楼

正如公认的答案所说,最惯用的方法是只使用一个模块。

考虑到这一点,这是概念证明:

def singleton(cls):
    obj = cls()
    # Always return the same object
    cls.__new__ = staticmethod(lambda cls: obj)
    # Disable __init__
    try:
        del cls.__init__
    except AttributeError:
        pass
    return cls


有关__new__的更多详细信息,请参见Python数据模型。

示例:

@singleton
class Duck(object):
    pass

if Duck() is Duck():
    print "It works!"
else:
    print "It doesn't work!"


注释:


为此,您必须使用新型类(从object派生)。
单例是在定义时进行初始化的,而不是在首次使用时进行初始化。
这只是一个玩具的例子。我从未在生产代码中实际使用过它,也不打算这样做。


评论


我尝试了此错误,但收到了错误消息:TypeError:必须以Integer实例作为第一个参数调用未绑定方法()(代替,请获取类型实例)我的Integer类是您的Duck类:@singleton类Integer(object):“” “整数类型为”“的对象的类传递

–汤姆·普拉茨(Tom Prats)
13年3月11日在7:03

感谢您指出了这一点。我不知道为什么会这样,但是编辑后的版本应该可以在Python 2.7和3.3上运行。

– Lambda Fairy
13年3月12日在8:02



这不好,在定义类时会调用__init __()方法(您可能要等到第一次使用它时),然后在每次调用Duck()时都将调用__init __()方法。

–tiho
13年4月10日在17:46

我已经记录了第一个问题,并修复了第二个问题。感谢您指出。

– Lambda Fairy
13年4月13日在7:54

#8 楼

Python文档确实涵盖了以下内容:

class Singleton(object):
    def __new__(cls, *args, **kwds):
        it = cls.__dict__.get("__it__")
        if it is not None:
            return it
        cls.__it__ = it = object.__new__(cls)
        it.init(*args, **kwds)
        return it
    def init(self, *args, **kwds):
        pass


我可能会重写它,使其看起来更像这样:

class Singleton(object):
    """Use to create a singleton"""
    def __new__(cls, *args, **kwds):
        """
        >>> s = Singleton()
        >>> p = Singleton()
        >>> id(s) == id(p)
        True
        """
        self = "__self__"
        if not hasattr(cls, self):
            instance = object.__new__(cls)
            instance.init(*args, **kwds)
            setattr(cls, self, instance)
        return getattr(cls, self)

    def init(self, *args, **kwds):
        pass


应该比较干净:

class Bus(Singleton):
    def init(self, label=None, *args, **kwds):
        self.label = label
        self.channels = [Channel("system"), Channel("app")]
        ...


评论


+1是唯一提及Guido van Rossum实施的人。但是,您自己的版本是错误的:您不应该在__new__内使用hasattr和getattr,因为它们都调用object .__ getattribute__,而这又会在所有类层次结构中而不是仅在当前类中查找您的“ __self__”属性。如果Guido使用__dict__进行属性访问是有原因的。尝试:A类(GuidoSingleton):通过,B(A)类:通过,C类(您的单身):通过,D(C)类:通过,print(A(),B(),C(),D( ))。所有子类都使用YourSingleton引用同一实例!

–马吉耶罗
17年12月15日在16:56

+1提醒我们,Python文档始终是开始搜索单例和其他设计模式的最佳场所。

–user-asterix
18年11月13日,0:58



#9 楼

我对此不太确定,但是我的项目使用的是“约定单例”(不是强制性的单例),也就是说,如果我有一个名为DataController的类,那么我将在同一模块中对其进行定义:

_data_controller = None
def GetDataController():
    global _data_controller
    if _data_controller is None:
        _data_controller = DataController()
    return _data_controller


它不够优雅,因为它有完整的六行。但是我所有的单例都使用此模式,并且至少非常显式(这是pythonic的)。

评论


+1在Python中,所有内容都应与约定有关(因为您通常可以绕过强制边界)。就个人而言,我更喜欢使用classmethod和class变量来访问和存储实例,因此您不必使用global。 (尽管这是少数几个可以接受的用例之一,但我通常不建议使用global。)

– schlamar
13年1月11日在10:23

DataController应该是_DataController吗?否则可以直接实例化

–不
11月13日20:19

#10 楼

一次我用Python写单例时,我使用了一个类,其中所有成员函数都具有类方法装饰器。

class foo:
  x = 1

  @classmethod
  def increment(cls, y = 1):
    cls.x += y


评论


我喜欢这种方法,但是有一个小问题。至少在Python 2.6中,您无法使__len__或__getitem__之类的方法像类方法一样工作,因此自定义的灵活性不如对象。由于我经常想将Singleton用作数据集合,因此有点令人失望。

–丹·霍姆里克(Dan Homerick)
10年7月29日在23:19



在我看来,这不过是将一堆东西包装到命名空间中而已……并不是说这有什么问题,甚至有人说他们认为这是个很棒的主意(请输入此内容),只是这种方法不只是简单,而且似乎非常接近使用全局变量,这通常被认为是不好的工程实践。

–马丁内
2012-12-10 3:42



@martineau我建议无论如何实现,使用单例都非常接近于使用全局变量。

–大卫·洛克(David Locke)
2012年12月12日的16:11

单例在两个方面比全局变量要好:它们根本不会污染全局名称空间(或像您的回答一样多),并且它们还提供延迟的求值,而全局变量通常不这样做(您的回答也不会) )。

–马丁内
2012年12月12日在21:30

@DanHomerick用于__len __,__ getitem__甚至@property,您可以将__metaclass__设置为用于定义上述内容的类。做得好。我投票给一个类作为单例,这是由语言的设计决定的,是它的元类的实例。实际上,所有方法都可以在元类中定义,然后该类将用作对单例的引用

–莱昂尼德·乌索夫(Leonid Usov)
2015年8月5日在17:47



#11 楼

如果您要装饰(注释)以后的类,则创建单例装饰器(也称为注释)是一种优雅的方法。然后,您只需将@singleton放在类定义之前。

def singleton(cls):
    instances = {}
    def getinstance():
        if cls not in instances:
            instances[cls] = cls()
        return instances[cls]
    return getinstance

@singleton
class MyClass:
    ...


评论


我不知道为什么没有投票赞成?很好..请解释为什么以及如何调用getinstance方法?

–Yugal Jindle
2012-03-10 11:43

好像您已复制PEP318?

–Yugal Jindle
2012-03-10 11:53

@YugalJindle:仅供参考,这里的类装饰器函数将传递给它的类对象替换为一个函数,该函数将在第一次创建类时通过调用它返回创建的类的新实例,或第一个副本(如果不是第一次)。

–马丁内
2012年12月12日在20:22

这种方法的一个潜在的(虽然可能很小)问题是,类名最终将绑定到一个函数上,而不是一个类对象上。这意味着不可能使用普通的类Derived(MyClass)语句创建MyClass的子类。

–马丁内
2012年12月12日20:36



@tiho:出于某些原因,我不同意这是一个主要问题。存在的一些问题:至少可以通过两种方式轻松解决/解决问题,并且我认为创建类的主要原因是封装,而不是允许或支持继承,这在单例类中尤为如此。

–马丁内
13年4月10日在20:06

#12 楼

Google Testing博客上也有一些有趣的文章,讨论了为什么单身人士是/可能是坏人并且是反模式的原因:


单身是病态的骗子
哪里都有单例消失了吗?
单例的根本原因


评论


我将您的链接放在单独的行上,这样它们就不会全部合并为一个

–kͩeͣmͮpͥͩ
2011年11月1日,9:24

#13 楼

我认为将类或实例强制为单例是过分的。就个人而言,我喜欢定义一个普通的可实例化类,一个半私有引用和一个简单的工厂函数。

class NothingSpecial:
    pass

_the_one_and_only = None

def TheOneAndOnly():
    global _the_one_and_only
    if not _the_one_and_only:
        _the_one_and_only = NothingSpecial()
    return _the_one_and_only


如果在实例化实例化时没有问题,首先导入模块:

class NothingSpecial:
    pass

THE_ONE_AND_ONLY = NothingSpecial()


这样,您可以针对没有副作用的新鲜实例编写测试,并且无需在模块上散布全局语句,如果需要您将来可以派生变体。

评论


我认为这比矫kill过正更糟。强制隐式行为不是pythonic。我对重复的答案有更多评论:-) stackoverflow.com/questions/31875 / ...

– schlamar
2013年1月11日上午10:29

#14 楼

使用ActiveState由Python实现的Singleton模式。

技巧似乎是将应该只有一个实例的类放在另一个类中。

#15 楼

class Singleton(object[,...]):

    staticVar1 = None
    staticVar2 = None

    def __init__(self):
        if self.__class__.staticVar1==None :
            # create class instance variable for instantiation of class
            # assign class instance variable values to class static variables
        else:
            # assign class static variable values to class instance variables


评论


这个解决方案是传奇的,我很高兴我在这里漫步

–威洛里
9月21日晚上10:16

#16 楼

好吧,我知道单身可能是善是恶。这是我的实现,我只是简单地扩展了一种经典方法,即在内部引入缓存,并生成许多不同类型的实例,或者许多具有相同类型但具有不同参数的实例。

我将其称为Singleton_group ,因为它将相似的实例组合在一起,并阻止了可以创建具有相同参数的相同类的对象:

每个对象都承载单例缓存。 。这可能是邪恶的,但对某些人来说却很有用:)

#17 楼

我的简单解决方案基于函数参数的默认值。

def getSystemContext(contextObjList=[]):
    if len( contextObjList ) == 0:
        contextObjList.append( Context() )
        pass
    return contextObjList[0]

class Context(object):
    # Anything you want here


#18 楼

作为Python的新手,我不确定最常见的习惯用法是什么,但是我能想到的最简单的事情就是使用模块而不是类。类上的实例方法原本只是模块中的函数,而任何数据都只是模块中的变量,而不是类的成员。我怀疑这是解决人们使用单例的问题类型的pythonic方法。

如果您真的想要单例类,那么Google的第一个热门版本就描述了一个合理的实现,“ Python单例” ”,具体是:

class Singleton:
    __single = None
    def __init__( self ):
        if Singleton.__single:
            raise Singleton.__single
        Singleton.__single = self


这似乎可以解决问题。

评论


不好,只是引发异常而不是返回单例实例

– pylover
2013年1月30日22:50

#19 楼

Singleton的同父异母兄弟

我完全同意staale,这里留下创建一个Singleton的同父异母兄弟的示例:

class void:pass
a = void();
a.__class__ = Singleton


a将立即报告与单例类相同,即使看起来不像。因此,使用复杂类的单例最终取决于我们对它们的了解不多。

这样,我们可以具有相同的效果,并可以使用更简单的东西,例如变量或模块。不过,如果我们要使用类来保持清晰,并且因为在Python中类是一个对象,那么我们已经有了该对象(没有和实例,但它会像这样)。

class Singleton:
    def __new__(cls): raise AssertionError # Singletons can't have instances


如果尝试创建实例,则会出现一个很好的断言错误,并且可以存储派生静态成员并在运行时对其进行更改(我喜欢Python)。这个对象和其他同父异母的兄弟一样好(如果需要,您仍然可以创建它们),但是由于简单性,它往往会运行得更快。

#20 楼

class Singeltone(type):
    instances = dict()

    def __call__(cls, *args, **kwargs):
        if cls.__name__ not in Singeltone.instances:            
            Singeltone.instances[cls.__name__] = type.__call__(cls, *args, **kwargs)
        return Singeltone.instances[cls.__name__]


class Test(object):
    __metaclass__ = Singeltone


inst0 = Test()
inst1 = Test()
print(id(inst1) == id(inst0))


#21 楼

如果您不希望上面的基于元类的解决方案,并且不喜欢基于简单的函数装饰器的方法(例如,因为在那种情况下,单例类上的静态方法将不起作用),则可以采用以下折衷方法:

class singleton(object):
  """Singleton decorator."""

  def __init__(self, cls):
      self.__dict__['cls'] = cls

  instances = {}

  def __call__(self):
      if self.cls not in self.instances:
          self.instances[self.cls] = self.cls()
      return self.instances[self.cls]

  def __getattr__(self, attr):
      return getattr(self.__dict__['cls'], attr)

  def __setattr__(self, attr, value):
      return setattr(self.__dict__['cls'], attr, value)