为啥 Borg 模式比 Python 中的 Singleton 模式更好

Posted

技术标签:

【中文标题】为啥 Borg 模式比 Python 中的 Singleton 模式更好【英文标题】:Why is the Borg pattern better than the Singleton pattern in Python为什么 Borg 模式比 Python 中的 Singleton 模式更好 【发布时间】:2010-11-22 01:11:18 【问题描述】:

为什么Borg pattern 比Singleton pattern 更好?

我问是因为我看不出它们有什么不同。

博格:

class Borg:
  __shared_state = 
  # init internal state variables here
  __register = 
  def __init__(self):
    self.__dict__ = self.__shared_state
    if not self.__register:
      self._init_default_register()

单例:

class Singleton:
  def __init__(self):
    # init internal state variables here
    self.__register = 
    self._init_default_register()

# singleton mechanics external to class, for example this in the module
Singleton = Singleton()

我想在这里展示的是服务对象,无论是作为 Borg 还是 Singleton 实现,都有一个重要的内部状态(它提供一些基于它的服务)(我的意思是它必须是有用的东西,它不是 Singleton/博格只是为了好玩)。

而且这个状态必须被初始化。这里的 Singleton 实现更直接,因为我们将 init 视为全局状态的设置。我觉得 Borg 对象必须查询其内部状态以查看它是否应该更新自己,这很尴尬。

你的内在状态越多,情况就越糟糕。例如,如果对象必须侦听应用程序的拆卸信号才能将其寄存器保存到磁盘,则该注册也应该只完成一次,而这对于单例来说更容易。

【问题讨论】:

博格模式? ^_^ 我第一次听说它是c2.com/cgi/wiki?MonostatePattern 单态?我们是马泰利斯。我们说博格。 这里对 Borg 的一些赞美:code.activestate.com/recipes/66531 【参考方案1】:

borg 与众不同的真正原因在于子类化。

如果您将一个 borg 子类化,则子类的对象与其父类对象具有相同的状态,除非您明确覆盖该子类中的共享状态。单例模式的每个子类都有自己的状态,因此会产生不同的对象。

同样在单例模式中,对象实际上是相同的,而不仅仅是状态(即使状态是唯一真正重要的东西)。

【讨论】:

> 同样在单例模式中,对象实际上是相同的,而不仅仅是状态(即使状态是唯一真正重要的东西)。为什么这是一件坏事? 好问题 uswaretech,这是我上面问题的一部分。说什么不好? 我没有说这是一件坏事。这是对差异的无意见观察。对困惑感到抱歉。有时单例实际上会更好,例如,如果您通过 id(obj) 对对象 id 进行任何检查,即使这种情况很少见。 那么python中的None是Singleton而不是Borg模式的例子? @ChangZhao:因为在 None 的情况下,我们需要有相同的身份,而不仅仅是共享状态。我们做x is None 检查。此外,None 是一种特殊情况,因为我们无法创建 None 的子类。【参考方案2】:

在 python 中,如果您想要一个可以从任何地方访问的唯一“对象”,只需创建一个仅包含静态属性、@staticmethods 和@classmethods 的类Unique;你可以称之为独特的模式。这里我实现并比较了 3 种模式:

独一无二

#Unique Pattern
class Unique:
#Define some static variables here
    x = 1
    @classmethod
    def init(cls):
        #Define any computation performed when assigning to a "new" object
        return cls

单身

#Singleton Pattern
class Singleton:

    __single = None 

    def __init__(self):
        if not Singleton.__single:
            #Your definitions here
            self.x = 1 
        else:
            raise RuntimeError('A Singleton already exists') 

    @classmethod
    def getInstance(cls):
        if not cls.__single:
            cls.__single = Singleton()
        return cls.__single

博格

#Borg Pattern
class Borg:

    __monostate = None

    def __init__(self):
        if not Borg.__monostate:
            Borg.__monostate = self.__dict__
            #Your definitions here
            self.x = 1

        else:
            self.__dict__ = Borg.__monostate

测试

#SINGLETON
print "\nSINGLETON\n"
A = Singleton.getInstance()
B = Singleton.getInstance()

print "At first B.x =  and A.x = ".format(B.x,A.x)
A.x = 2
print "After A.x = 2"
print "Now both B.x =  and A.x = \n".format(B.x,A.x)
print  "Are A and B the same object? Answer: ".format(id(A)==id(B))


#BORG
print "\nBORG\n"
A = Borg()
B = Borg()

print "At first B.x =  and A.x = ".format(B.x,A.x)
A.x = 2
print "After A.x = 2"
print "Now both B.x =  and A.x = \n".format(B.x,A.x)
print  "Are A and B the same object? Answer: ".format(id(A)==id(B))


#UNIQUE
print "\nUNIQUE\n"
A = Unique.init()
B = Unique.init()

print "At first B.x =  and A.x = ".format(B.x,A.x)
A.x = 2
print "After A.x = 2"
print "Now both B.x =  and A.x = \n".format(B.x,A.x)
print  "Are A and B the same object? Answer: ".format(id(A)==id(B))

输出:

单身

At first B.x = 1 and A.x = 1
After A.x = 2
Now both B.x = 2 and A.x = 2

Are A and B the same object? Answer: True

BORG

At first B.x = 1 and A.x = 1
After A.x = 2
Now both B.x = 2 and A.x = 2

Are A and B the same object? Answer: False

UNIQUE

At first B.x = 1 and A.x = 1
After A.x = 2
Now both B.x = 2 and A.x = 2

Are A and B the same object? Answer: True

在我看来,Unique 实现是最简单的,然后是 Borg,最后是 Singleton,其定义所需的两个函数数量非常多。

【讨论】:

【参考方案3】:

事实并非如此。在python中一般不推荐这样的模式:

class Singleton(object):

 _instance = None

 def __init__(self, ...):
  ...

 @classmethod
 def instance(cls):
  if cls._instance is None:
   cls._instance = cls(...)
  return cls._instance

您使用类方法而不是构造函数来获取实例。 Python 的元编程允许更好的方法,例如Wikipedia上的那个:

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

print MyClass()
print MyClass()

【讨论】:

+1 Monostate (Borg) 模式比 Singleton(是的,有可能)更糟糕,因为 private a = new Borg();私人 b = 新博格(); b.mutate();并且a变了!这有多令人困惑? 最好/更差?那将取决于您的用例,不是吗。我可以想到一些您希望像这样保留状态的情况。 这不是问题,@MichaelDeardeuff。这是预期行为。他们应该是一样的。恕我直言,borg 模式中的一个问题是,如果您在 Borg.__init__ 方法中添加一些初始化变量,例如 self.text = "",然后像 borg1.text = "blah" 一样更改该对象,然后实例化一个新对象 `borg2 = Borg()" -哇!在 init 中初始化的所有 borg1 属性都被鞭打了。所以实例化是不可能的 - 或者更好:在 Borg 模式中,你不能在 init 方法中初始化成员属性! 这在单例中是可能的,因为有一个if 检查是否已经有一个实例,如果是,它只是在没有覆盖初始化的情况下返回! 同样可以(并且应该)在 Borg init 中完成。 if __monostate: return 然后做你的 self.foo='bar'【参考方案4】:

一个类基本上描述了如何访问(读/写)对象的内部状态。

在单例模式中,您只能拥有一个类,即您的所有对象都会为您提供共享状态的相同访问点。 这意味着如果您必须提供扩展 API,则需要编写一个包装器,将单例包装起来

在 borg 模式中,您可以扩展基础“borg”类,从而更方便地扩展 API。

【讨论】:

【参考方案5】:

只有在您确实有差异的少数情况下才会更好。就像你的子类一样。 Borg 模式非常不寻常,在十年的 Python 编程中我从未真正需要它。

【讨论】:

【参考方案6】:

此外,类 Borg 模式允许类的用户选择是要共享状态还是创建单独的实例。 (这是否是一个好主意是一个单独的主题)

class MayBeBorg:
    __monostate = None

    def __init__(self, shared_state=True, ..):
        if shared_state:

            if not MayBeBorg.__monostate:
                MayBeBorg.__monostate = self.__dict__
            else:
                self.__dict__ = MayBeBorg.__monostate
                return
        self.wings = ..
        self.beak = ..

【讨论】:

以上是关于为啥 Borg 模式比 Python 中的 Singleton 模式更好的主要内容,如果未能解决你的问题,请参考以下文章

python 与Borg包装的python中的湖问题(多目标)

lambda 比 python 中的函数调用慢,为啥

为啥以下简单的并行化代码比 Python 中的简单循环慢得多?

为啥这个算法在 python 中的运行速度比在 C++ 中快得多?

为啥我的线性搜索比我在 Python3 中的二分搜索运行得更快?

为啥我的基数排序 python 实现比快速排序慢?