« Preliminary ruling: Eolas patent invalid | Main | Celestial Jukebox in Japan and South Korea? »

March 11, 2004

Comments

Doug

Here's one from Michele Simionato that uses metaclasses:

class Singleton(type):
def __init__(cls,name,bases,dic):
super(Singleton,cls).__init__(name,bases,dic)
cls.instance=None
def __call__(cls,*args,**kw):
if cls.instance is None:
cls.instance=super(Singleton,cls).__call__(*args,**kw)
return cls.instance

#example:
class C:
__metaclass__=Singleton

Gary Robinson

That's really interesting! I haven't used metaclasses yet. I'm going to look them up for the first time because of the above comment. I may have more to say after I do...

Gary Robinson

I now use metaclasses in my singleton class, still linked to at the same link. They help!

Lorenzo

Not a sw engineer myself but think that this example of Singleton is very neat:

http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66531

Gary Robinson

Hey, that _is_ really neat. Thanks for pointing it out. I don't have time now to consider its pros and cons in depth re more traditional singletons. I'll probably add a comment here when I do. In the meantime, I suggest anyone who is looking for a singleton and ends up on this page to check out the link posted in the previous comment. It really refers to the so-called "Borg" pattern, which not really a standard Singleton, but more of an alternative to a Singleton.

Gary Robinson

I do have one reflection now. I think that using the Borg pattern is less self-documenting than using the Singleton pattern, and not only because it is different from what people think of when they think of the properties of a Singleton, but also because it creates Python objects that don't work the way Python objects normally work (they don't normally share state).

I think those objections could be worked around in an actual program by using a standard naming convention for Borg classes, supplying documentation, and making sure everyone understands it. Whether it would be worth it or not, I don't know.

Benjamin Carlyle

With Gary's permission I have begun using his code in an embryonic accounting package (http://sourceforge.net/projects/transactionsafe). The simplicity of using it for my classes impressed me no end, and so far it has made the development of this week's release easier.

I'm a very new python user, and the main thing that scared off the borg pattern was that I couldn't work out how to ensure the object state was initialised only once. In my case I have singletons depending on other singletons to provide a data model for the application and an observer pattern set-up between them. This singleton pattern was easy to integrate with my code and to understand the rammifications of.

Benjamin.

Gary Robinson

One note, nobody needs my permission to use singletonmixin.py, which benefited from a lot of input from other people. (I would appreciate it, though, if you provide a link to this Web page and a comment here if you use it, however!)

I'm glad you are finding it useful, Benjamin!

Douglas

Gary:

I want to use your singleton class to build a DB object, but I'm not so sure how to do it. Is it possible? If it is, could you please provide an example of how doing this (when you have some time of course). Thanks.

Regards, Douglas

Steve Freitas

Hi Gary. While this looks like the most well thought out Singleton implementation I've found in Python, I'm having trouble making it work. To wit:

Python 2.3.4 (#2, Jan 5 2005, 08:24:51)
[GCC 3.3.5 (Debian 1:3.3.5-5)] on linux2
>>> from singletonmixin import Singleton
>>> class Foo(Singleton):
... pass
...
>>> m = Foo.getInstance()
Traceback (most recent call last):
File "", line 1, in ?
File "singletonmixin.py", line 78, in getInstance
if len(lstArgs) != cls._getConstructionArgCountNotCountingSelf():
File "singletonmixin.py", line 91, in _getConstructionArgCountNotCountingSelf
return cls.__init__.im_func.func_code.co_argcount - 1
AttributeError: 'wrapper_descriptor' object has no attribute 'im_func'
>>>

I don't understand the inner workings of metaclasses well enough to quickly fix this, so could you shed some light on what the problem might be?

Thanks!

Steve Freitas

Gary Robinson

I hadn't thought about this earlier, but it looks like you need to have a constructor for your class, even if that constructor is empty. The following version works with no problems:


>>> class Foo(Singleton):
... def __init__(self):
... pass
...
>>> m = Foo.getInstance()
>>>

Michal Eldar

Hi,
I really like this implementation!!
About the lstArgs in getInstance, it seems it doesn't handle defaults :(

I wonder if anyone has a solution for it, or should I get rid of the error checking?!?!

I found out I can check the amount of defaults with .im_func.func_defaults, but then the number of args can vary, what makes it more complicated...

Gary Robinson

Thanks Michal for pointing this out. It's fixed in the latest version, uploaded today.

Ethan Glasser-Camp

Hey,

I found your singleton really useful, but I made some modifications to it. Biggest is that I made getInstance ignore its args if the object was already constructed (rather than throw an exception saying "It's already constructed, your arguments are meaningless"). The reason is because in this application, I'm unable to construct the object until late in the program, and I didn't want to have to check in the main code whether the singleton had already been instantiated, and pass args accordingly. (Note that there's no public way to check if the thing has been instantiated anyhow.)

Secondly, I changed the MetaSingleton.__call__ function to just delegate the call to cls.getInstance, in other words, re-enabled the S() constructor. At this point, getInstance already takes arguments, so it behaves pretty much like a constructor anyhow. I, the developer, knew that I was using a singleton in these situations, and thought it was silly to force use of one function to construct/get an instance versus another.

Thanks again.

Ethan

Darran Edmundson

Hi Gary, we're now using the singleton mixin in our internal VR code, espace - and the key reason is that it comes with unit tests ;-) Thanks for releasing this little gem with no strings atttached. I'll buy you a beer if we ever meet up.

Marcus Goldfish

Gary,
Thanks for posting this nice singleton code-- it's exactly what I needed for a project I am working on.

I did have to modify the code: I had a class with a keyword default argument in the constructor getInstance() didn't work. I used the im_func.func_defaults trick posted above.

Thanks again,
Marcus

Gordon Morehouse

Is this broken in Python 2.5? PyDev is flagging it with errors and I'm having trouble getting it to work.

DDT-WEBKINZ

Thanks for your work! Now let's see how you'll interact with all the junk coming from social blogs, social networks, social software and the whole good load from web 2.0 ;) Tough luck on that!

Lonnie Hutchinson

Thanks for this great utility. I ran into a problem with it since I have a class that takes keyword args, and the arity check was failing. I've updated the code to handle this case by taking both *args and **kwargs and invoking __init__ with them. This changes the exception that is raised when the specified args aren't sufficiently specified, so the unit test also changed to reflect that.

--- singletonmixin.py-orig	2009-07-22 08:55:28.000000000 -0700
+++ singletonmixin.py-new	2009-07-22 09:00:22.000000000 -0700
@@ -70,20 +70,18 @@
 class Singleton(object):
     __metaclass__ = MetaSingleton
     
-    def getInstance(cls, *lstArgs):
+    def getInstance(cls, *args, **kwargs):
         """
         Call this to instantiate an instance or retrieve the existing instance.
         If the singleton requires args to be instantiated, include them the first
         time you call getInstance.        
         """
         if cls._isInstantiated():
-            if len(lstArgs) != 0:
+            if args or kwargs:
                 raise SingletonException, 'If no supplied args, singleton must already be instantiated, or __init__ must require no args'
         else:
-            if cls._getConstructionArgCountNotCountingSelf() > 0 and len(lstArgs) <= 0:
-                raise SingletonException, 'If the singleton requires __init__ args, supply them on first instantiation'
             instance = cls.__new__(cls)
-            instance.__init__(*lstArgs)
+            instance.__init__(*args, **kwargs)
             cls.cInstance = instance
         return cls.cInstance
     getInstance = classmethod(getInstance)
@@ -92,10 +90,6 @@
         return hasattr(cls, 'cInstance')
     _isInstantiated = classmethod(_isInstantiated)  
 
-    def _getConstructionArgCountNotCountingSelf(cls):
-        return cls.__init__.im_func.func_code.co_argcount - 1
-    _getConstructionArgCountNotCountingSelf = classmethod(_getConstructionArgCountNotCountingSelf)
-
     def _forgetClassInstanceReferenceForTesting(cls):
         """
         This is designed for convenience in testing -- sometimes you 
@@ -163,7 +157,7 @@
                     self.arg1 = arg1
                     self.arg2 = arg2
 
-            self.assertRaises(SingletonException, B.getInstance)
+            self.assertRaises(TypeError, B.getInstance)
             
         def testTryToInstantiateWithoutGetInstance(self):
             """
Gary Robinson

Thanks Lonnie, Michal, and Marcus for mentioning the issue with keyword arguments, and especially to Lonnie for posting code!

The final version takes most of Lonnie's changes, but still checks to make sure that necessary args are included in the instantiating call to getInstance() by raising a SingletonException. It does this by checking for the TypeError Lonnie put into the unit test, but additionally checking the exception message to see if it corresponds to the missing arguments error. (A different technique than Michal and Marcus used.)

I'll mention here something I mentioned also in the update to the main post: the code now should be threadsafe for singleton creation. Of course your Singleton class may not itself be threadsafe; I can only control the creation!

Gary Robinson

Made an additional update to deal with Ethan's issue where he wanted to be able to call getInstance() multiple times with args, where the args are ignored in calls other than the first. To enable this functionality, just include

ignoreSubseqent = True

in the class definition.

James Stroud

Here is my favorite singleton implementation. It serves me quite well and is fluidly heritable:

class Singleton(object):
  instance = None
  def __new__(cls):
    if cls.instance is None:
      i = object.__new__(cls)
      cls.instance = i
    else:
      i = cls.instance
    return i
Jan Bessai

Inspired by your design I've created a decorator based approach.
I was unable to find [code] tags here, so please see
http://pastebin.com/f7834050

Jan Bessai

Please see
http://pastebin.com/f66169fef
for an updated version that comes in more handy for subsequent constructor calls and is transparent for docstrings.

Ivan Ricotti

Just thank you Gary! :)

Verify your Comment

Previewing your Comment

This is only a preview. Your comment has not yet been posted.

Working...
Your comment could not be posted. Error type:
Your comment has been posted. Post another comment

The letters and numbers you entered did not match the image. Please try again.

As a final step before posting your comment, enter the letters and numbers you see in the image below. This prevents automated programs from posting comments.

Having trouble reading this image? View an alternate.

Working...

Post a comment