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

March 11, 2004

Python Singleton mixin class

Here's my take on a Singleton mixin class for Python. Public domain. Based in part on ideas I found on this wiki page. If you find it useful and think of ways to improve it, please post a comment here.

Update: the Singleton source code that is linked to above is from our internal CVS tree; the code is used in our Goombah product. I will try to keep the version here in sync with our CVS version, so that any enhancements or fixes will be included. If any are made I'll also post a comment here. The code was last updated on May 31, 2005.

March 11, 2004 in Web/Tech | 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 2: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 1: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 2: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 10:43:46 AM

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 12: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 6: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 7: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 8: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 4: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 12: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 12: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 3: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 1: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 12: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 7, 2006 11:22:19 PM

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 5: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 11, 2007 9:56:10 PM

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 5:42:08 PM

Post a comment