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

March 11, 2004

Python Singleton mixin class

Here's a quite complete Singleton mixin class for Python, including such niceties as thread safety for singleton creation. Public domain. Based in part on ideas I found on this wiki page, followed by a substantial number of enhancements based on comments posted below and based on my own additional needs. If you find it useful and think of ways to improve it, please post a comment here.

Lately I've noticed some discussion of the evilness of singletons. One poster says:

It's almost impossible to subclass a Singleton, and if you manage it, then you shouldn't have been using a Singleton in the first place. You don't *even* want to go there. I've walked roads that I dare not recount. Just pretend you can't do it, and you'll save yourself amazing amounts of pain.

My singleton mixin source includes unit tests showing that at least rudimentary subclassing does work with the approach used here. I haven't run into any problems in real life either, although I use singletons sparingly. 

If anyone has examples or thoughts about problems with subclassing with this mixin, please post a comment.

Update, July 28, 2009: Now optionally allows multiple calls to getInstance() which all include arguments. See the docstring for more. Update, July 27, 2009: I try to keep the Singleton version here in sync with our svn version, so that any enhancements or fixes will be included. The code, still at the original link (above), has been modified today to add thread safety for singleton creation, and also incorporates ideas from some of the recent comments people have posted here. In particular, singleton construction can now handle keyword arguments.

March 11, 2004 in Python | Permalink

Comments

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

Posted by: Doug at Mar 11, 2004 5:31:11 PM

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

Posted by: Gary Robinson at Mar 12, 2004 4:01:13 PM

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

Posted by: Gary Robinson at Jun 8, 2004 5:13:24 PM

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

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

Posted by: Lorenzo at Sep 27, 2004 1:43:46 PM

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.

Posted by: Gary Robinson at Sep 27, 2004 3:43:31 PM

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.

Posted by: Gary Robinson at Sep 28, 2004 9:37:22 AM

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.

Posted by: Benjamin Carlyle at Sep 28, 2004 10:31:44 AM

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!

Posted by: Gary Robinson at Sep 28, 2004 11:04:09 AM

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

Posted by: Douglas at Dec 30, 2004 7:21:32 AM

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

Posted by: Steve Freitas at Jan 17, 2005 3:30:19 PM

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

Posted by: Gary Robinson at Jan 17, 2005 3:41:53 PM

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

Posted by: Michal Eldar at May 17, 2005 6:31:28 PM

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

Posted by: Gary Robinson at May 31, 2005 4:21:13 PM

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

Posted by: Ethan Glasser-Camp at Aug 2, 2005 3:41:58 PM

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.

Posted by: Darran Edmundson at May 8, 2006 2:22:19 AM

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

Posted by: Marcus Goldfish at Jun 25, 2006 8:03:26 PM

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

Posted by: Gordon Morehouse at Mar 12, 2007 12:56:10 AM

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!

Posted by: DDT-WEBKINZ at Oct 22, 2007 8:42:08 PM

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):
             """

Posted by: Lonnie Hutchinson at Jul 22, 2009 12:07:05 PM

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!

Posted by: Gary Robinson at Jul 27, 2009 10:43:48 PM

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.

Posted by: Gary Robinson at Jul 28, 2009 8:41:02 AM

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

Posted by: James Stroud at Oct 29, 2009 12:43:37 AM

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

Posted by: Jan Bessai at Nov 26, 2009 4:21:13 PM

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

Posted by: Jan Bessai at Nov 26, 2009 5:48:51 PM

Just thank you Gary! :)

Posted by: Ivan Ricotti at May 14, 2010 10:35:10 AM

Post a comment