ChatGPT解决这个技术问题 Extra ChatGPT

Why is the Borg pattern better than the Singleton pattern in Python

Why is the Borg pattern better than the Singleton pattern?

I ask because I don't see them resulting in anything different.

Borg:

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

Singleton:

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

What I want to display here is that the service object, whether implemented as Borg or Singleton, has a nontrivial internal state (it provides some service based on it) (I mean it has to be something useful it's not a Singleton/Borg just for fun).

And this state has to be inited. Here the Singleton implementation is more straightforward, since we treat init as the set-up of the global state. I find it awkward that the Borg object has to query its internal state to see if it should update itself.

It becomes worse the more internal state you have. For example, if the object has to listen to the Application's teardown signal to save its register to disk, that registration should only be done once as well, and this is easier with a Singleton.

Borg pattern? ^_^ I'd first heard of it as c2.com/cgi/wiki?MonostatePattern
Monostate? We are the Martellis. We say Borg.
Some praise for Borg here: code.activestate.com/recipes/66531

M
Mirco Widmer

The real reason that borg is different comes down to subclassing.

If you subclass a borg, the subclass' objects have the same state as their parents classes objects, unless you explicitly override the shared state in that subclass. Each subclass of the singleton pattern has its own state and therefore will produce different objects.

Also in the singleton pattern the objects are actually the same, not just the state (even though the state is the only thing that really matters).


> Also in the singleton pattern the objects are actually the same, not just the state (even though the state is the only thing that really matters). Why is that a bad thing?
good question uswaretech, it is a part of my question above. What is that said as bad?
I did not say it was a bad thing. It was an opinion-less observation on the differences. Sorry for the confusion. Sometimes the singleton will be better infact, if for example you do any checks on the objects id by id(obj), even though this is rare.
Then how None in python is a Singleton and not an example of Borg pattern ?
@ChangZhao: because in the case of None we need to have the same identity, not only share state. We do x is None checkings. Furthermore, None is an special case as we cannot create subclasses of None.
C
Cristian Garcia

In python if you want a unique "object" that you can access from anywhere just create a class Unique that only contains static attributes, @staticmethods, and @classmethods; you could call it the Unique Pattern. Here I implement and compare the 3 patterns:

Unique

#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

#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

#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

Test

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

Output:

SINGLETON 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

In my opinion, Unique implementation is the easiest, then Borg and finally Singleton with an ugly number of two functions needed for its definition.


A
André Laszlo

It is not. What is generally not recommended is a pattern like this in python:

class Singleton(object):

 _instance = None

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

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

where you use a class method to get the instance instead of the constructor. Python's metaprogramming allows much better methods, e.g. the one on 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 The Monostate (Borg) pattern is worse than Singleton (yes, it's possible) because private a = new Borg(); private b = new Borg(); b.mutate(); and a is changed! How confusing is that?
Best/ worse? That would depend on your usecase wouldn't it. I can think of a number of cases where you would want state preserved like that.
This is not a problem, @MichaelDeardeuff. This is intended behaviour. They Should be the same. A problem IMHO in the borg pattern is, that if you add some initialisation variables in the Borg.__init__ method, like self.text = "", then change that object like borg1.text = "blah" and then instantiate a new object `borg2 = Borg()" - wham! all borg1 attributes that are initialized in init are whiped. so instantiating is impossible - or better: In the Borg pattern, you MAY NOT initialize member attributes in the init method!
This is possible in a Singleton because there is an if check wether there is already an instance, and if yes, it is just returned WITHOUT an overriding initialisation!
same can be (and should be) done in Borg init. if __monostate: return and after that do your self.foo='bar'
Z
Zed

A class basically describes how you can access (read/write) the internal state of your object.

In the singleton pattern you can only have a single class, i.e. all your objects will give you the same access points to the shared state. This means that if you have to provide an extended API, you will need to write a wrapper, wrapping around the singleton

In the borg pattern you are able to extend the base "borg" class, and thereby more conveniently extend the API for your taste.


L
Lennart Regebro

It's only better in those few cases where you actually have a difference. Like when you subclass. The Borg pattern is extremely unusual, I've never needed it for real in ten years of Python programming.


v
volodymyr

Also, Borg-like pattern allows users of the class to choice if they want to share the state or create a separate instance. (whether or not this may be a good idea is a separate topic)

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