Borg vs Singleton vs OddClass

L

Lie

This is probably unrelated to Python, as this is more about design
pattern. I'm asking your comments about this design pattern that is
similar in functionality to Singleton and Borg: to share states.

I'm thinking about this design pattern (I don't know if anyone has
ever thought of this pattern before):

class OddClass(object):
def __init__(self):
global OddClass
OddClass = self
def __call__():
return self

The OddClass is a class that would overwrite its own class definition
at its first instantiation. OddClass defines __call__ so that
subsequent "instantiation" (technically it is no more an
instantiation, but Duck Typing says it does) of the class would return
the single instance.

It do have a problem though, that you can't do isinstance(a, OddClass)
since the name OddClass no longer refers to the OddClass class
descriptor, but to an instance of OddClass. I don't think that's much
of a problem though since using isinstance() is generally not a good
idea in python (although OddClass do use global which many people,
including me, would usually consider as bad form).

The problem with Borg is that it is not inheritable (in certain
sense[1]) and only work with old-style class (which is due to be
completely removed on py3k)[2], Singleton and OddClass is inheritable.
.... Traceback, Attribute Error ...
.... Traceback, Attribute Error ...

but for Borg, see [1]

[1] classes that inherits from Borg shares state not only within
children, but also with their parents and cousins. That means
inheriting from Borg is useless, and that also means one Borg code for
every state sharing classes, instead of inheriting from Singleton/
OddClass. In code:
[2] Actually in new-style class, they say Borg can use __slots__, but
they say it'd be slightly more complex
 
M

Miles

Lie said:
This is probably unrelated to Python, as this is more about design
pattern. I'm asking your comments about this design pattern that is
similar in functionality to Singleton and Borg: to share states.

I'm thinking about this design pattern (I don't know if anyone has
ever thought of this pattern before):

class OddClass(object):
def __init__(self):
global OddClass
OddClass = self
def __call__():

I'll change this to def __call__(self):
return self

The OddClass is a class that would overwrite its own class definition
at its first instantiation. OddClass defines __call__ so that
subsequent "instantiation" (technically it is no more an
instantiation, but Duck Typing says it does) of the class would return
the single instance.

This seems like a terrible idea to me, but then I never really
understood the appeal of the Singleton pattern, especially in Python.
Singleton and OddClass is inheritable.

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
__init__() takes exactly 1 argument (4 given)

Oops! And assuming you carefully ordered your code so that OddClass
will never be instantiated before it is subclassed (which seems
fragile), you get behavior like this:
False

-Miles
 
S

Steven D'Aprano

This is probably unrelated to Python, as this is more about design
pattern. I'm asking your comments about this design pattern that is
similar in functionality to Singleton and Borg: to share states.

I'm thinking about this design pattern (I don't know if anyone has ever
thought of this pattern before):

class OddClass(object):
def __init__(self):
global OddClass
OddClass = self
def __call__():
return self


I don't think that pattern works as you give it. I suggest you read the
thread "What do you call a class not intended to be instantiated",
started by me on the 21st of September, which covers a similar pattern.

I'm afraid it's a rather long thread, with a lot of people
misunderstanding what I was asking, but still worth reading. If you only
have time to read one post, I suggest you read my reply to Ben Finney,
posted yesterday.

My own feeling is that both your OddClass and my class without instances
are probably solutions looking for a problem. Well, actually, no, that's
too strong: I think the concept of "Class Singleton" is a perfectly valid
solution to certain problems, but it competes with more well-known
solutions like modules and Borg (in Python) and Singletons (the hammer
available in Java and C++). As for which is better, that's partly a
matter of personal taste and partly a matter of familiarity.

It do have a problem though, that you can't do isinstance(a, OddClass)

But you can say "a is OddClass", which is more appropriate for a
Singleton.
The problem with Borg is that it is not inheritable (in certain
sense[1]) and only work with old-style class (which is due to be
completely removed on py3k)[2]

No, there is a new-style Borg. Read the comments here:
http://code.activestate.com/recipes/66531/

The new-style Borg is hardly more complicated than old-style: 6 lines
versus 4.

I like Luke Plant's comment:

"classes and modules are singletons. You don't need singletons in python
simply because classes and modules are always singletons, and they are
also first class objects. They can have everything instances have, and as
import statements don't make copies there is only ever one of them. We
don't need no stinkin' design patterns."
 
C

Carl Banks

I'm thinking about this design pattern (I don't know if anyone has
ever thought of this pattern before):

class OddClass(object):
    def __init__(self):
        global OddClass
        OddClass = self
    def __call__():
        return self

The OddClass is a class that would overwrite its own class definition
at its first instantiation. OddClass defines __call__ so that
subsequent "instantiation" (technically it is no more an
instantiation, but Duck Typing says it does) of the class would return
the single instance.

It do have a problem though, that you can't do isinstance(a, OddClass)
since the name OddClass no longer refers to the OddClass class
descriptor, but to an instance of OddClass.

I recommend against your idiom above. The main issue I have about it
is that it rebinds the symbol implicitly, which is almost always a bad
idea. What if a user does something like "from oddclassmodule import
OddClass"? Then the user will get a new instance every call since it
never rebinds the imported symbol.

Just don't do it this way.

You could rewrite it like this to avoid the implicit rebinding, and to
take care of the isinstance issue as well:

class NotSoOddClass(object):
def __new__(cls):
self = getattr(cls,"_instance",None)
if self is None:
self = cls._instance = object.__new__(cls)
return self


Or you could just use a lazy factory function like this, where the
user is only supposed to use Factory and not create the class
directly:

class _NotOddClass(object):
# nothing odd

def Factory():
obj = getattr(_NotOddClass,"_instance",None)
if obj is None:
obj = _NotOddClass._instance = NotOddClass()
return obj


If you're real kinky you can use a metaclass. There are reasons to
prefer any of these. I'd recommend the factory function unless you
think the users could significantly benefit from type inspection.

Just don't do it by rebinding the class name. That's not nice.


Carl Banks
 
L

Lie

I'll change this to def __call__(self):



This seems like a terrible idea to me, but then I never really
understood the appeal of the Singleton pattern, especially in Python.


Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    __init__() takes exactly 1 argument (4 given)

Oops!  And assuming you carefully ordered your code so that OddClass
will never be instantiated before it is subclassed (which seems
fragile), you get behavior like this:

I test the code what would happen if I do this before posting the
pattern:
It doesn't give me errors, where are you having the problem?

Well, spotted, there is identity problem with this pattern.
 
L

Lie

I don't think that pattern works as you give it. I suggest you read the
thread "What do you call a class not intended to be instantiated",
started by me on the 21st of September, which covers a similar pattern.

In fact, that thread inspired this thread.
I'm afraid it's a rather long thread, with a lot of people
misunderstanding what I was asking, but still worth reading. If you only
have time to read one post, I suggest you read my reply to Ben Finney,
posted yesterday.

.... before I decided probably this pattern is probably isn't the
answer to that thread.
My own feeling is that both your OddClass and my class without instances
are probably solutions looking for a problem. Well, actually, no, that's
too strong: I think the concept of "Class Singleton" is a perfectly valid
solution to certain problems, but it competes with more well-known
solutions like modules and Borg (in Python) and Singletons (the hammer
available in Java and C++). As for which is better, that's partly a
matter of personal taste and partly a matter of familiarity.
It do have a problem though, that you can't do isinstance(a, OddClass)

But you can say "a is OddClass", which is more appropriate for a
Singleton.
The problem with Borg is that it is not inheritable (in certain
sense[1]) and only work with old-style class (which is due to be
completely removed on py3k)[2]

No, there is a new-style Borg. Read the comments here:http://code.activestate.com/recipes/66531/

The new-style Borg is hardly more complicated than old-style: 6 lines
versus 4.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top