6

I'm learning Python and i've been trying to implement a Singleton-type class as a test. The code i have is as follows:

_Singleton__instance = None

class Singleton:
    def __init__(self):
        global __instance
        if __instance == None:           
            self.name = "The one"
            __instance = self
        else:
            self = __instance

This works in part but the self = __instance part seems to be failing. I've included some output from the interpretor to demonstrate (the code above is saved in singleton.py):

>>> import singleton
>>> x = singleton.Singleton()
>>> x.name
'The one'
>>> singleton._Singleton__instance.name
'The one'
>>> y = singleton.Singleton()
>>> y.name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Singleton instance has no attribute 'name'
>>> type(y)
<type 'instance'>
>>> dir(y)
['__doc__', '__init__', '__module__']

Is it possible to do what i'm trying? If not is there another way of doing this?

Any suggestions welcome.

Cheers.

1
  • 1
    Other than an interesting learning exercise, do you have a use case for this Singleton? Sometimes Singletons can be much more simply than this. The Singleton design pattern -- to an extent -- is a Smalltalk/C++/Java-ism that isn't often needed in Python. Commented Sep 1, 2009 at 18:50

5 Answers 5

22

Assigning to an argument or any other local variable (barename) cannot ever, possibly have ANY effect outside the function; that applies to your self = whatever as it would to ANY other assignment to a (barename) argument or other local variable.

Rather, override __new__:

class Singleton(object):

    __instance = None

    def __new__(cls):
        if cls.__instance == None:
            cls.__instance = object.__new__(cls)
            cls.__instance.name = "The one"
        return cls.__instance

I've done other enhancements here, such as uprooting the global, the old-style class, etc.

MUCH better is to use Borg (aka monostate) instead of your chosen Highlander (aka singleton), but that's a different issue from the one you're asking about;-).

Sign up to request clarification or add additional context in comments.

6 Comments

Eheh, SO is sponsoring his (great) creature here :) I agree, of course
Thanks for that! I guess i haven't got my head around the init method as much as i thought i had; i was fooled by the self.name effecting the object but i guess this is a bit of Python magic?. The question was more about understanding Python than using the Singleton pattern but i'll check out the Borg link anyway :)
self.name is a QUALIFIED name, not a BARENAME: assigning to a barename and assigning to a qualified name are potentially ENTIRELY different operations -- I devote QUITE a bit of space to that in the appropriate chapters of "Python in a Nutshell", too much to repeat everything here, so I suggest you read that (maybe from one of the many pirate copies on the net: as the author I'm NOT happy about them, but there sure ARE a huge number!-)
With 2.6 on Debian squeeze, I get "TypeError: type.__new__(Singleton): Singleton is not a subtype of type" when trying to instantiate this class.
Maybe I'm missing something, but shouldn't each __instance be cls.__instance and type should be object ??
|
6

Bruce Eckel's code snippet from Design Pattern: I'm confused on how it works

class Borg:
  _shared_state = {}
  def __init__(self):
    self.__dict__ = self._shared_state

class MySingleton(Borg):
  def __init__(self, arg):
    Borg.__init__(self)
    self.val = arg
  def __str__(self): return self.val

x = MySingleton('sausage')
print x
y = MySingleton('eggs')
print y
z = MySingleton('spam')
print z
print x
print y
print ´x´
print ´y´
print ´z´
output = '''
sausage
eggs
spam
spam
spam
<__main__. MySingleton instance at 0079EF2C>
<__main__. MySingleton instance at 0079E10C>
<__main__. MySingleton instance at 00798F9C>
'''

1 Comment

+1 for this reference. In particular, see this answer: stackoverflow.com/questions/1296311/…
4

From Singleton Pattern (Python):

class Singleton(type):
    def __init__(self, name, bases, dict):
        super(Singleton, self).__init__(name, bases, dict)
        self.instance = None

    def __call__(self, *args, **kw):
        if self.instance is None:
            self.instance = super(Singleton, self).__call__(*args, **kw)

        return self.instance

class MyClass(object):
    __metaclass__ = Singleton

print MyClass()
print MyClass()

2 Comments

why does you Singleton inherit "type"? What is type? Why not object?
This way of implementing Singleton calls init twice, which caused me a lot of headache, overriding modified variables back to the original initializations.
3

This is about the most basic Singleton you can make. It uses a class method to check whether the singleton has been created and makes a new one if it hasn't. There are more advanced ways of going about this, such as overriding the __new__ method.

class Singleton:
    instance = None

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

    def __init__(self):
        self.x = 5       # or whatever you want to do

sing = Singleton.get()
print sing.x  # prints 5

As for why your code fails, there are several reasons. First, by the time __init__ is called, a new object has already been created, defeating the purpose of the singleton pattern. Second, when you say self = __instance, that simply resets the local variable self; this would be akin to saying

def f(x):
  x = 7    # changes the value of our local variable

y = 5
f(y)
print y   # this is still 5

Since variables in Python are passed by value and not reference, you can't say self = blah and have it be meaningful in the way you want. The above Singleton class is more what you want, unless you want to get fancy and look into overriding the __new__ operator.

1 Comment

they are passed by sharing and not by reference, value: effbot.org/zone/call-by-object.htm.
0
self = _instance

This wont do what you are expecting it to do. Read about how Python treats names.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.