views:

344

answers:

5

Hi,

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.

+3  A: 

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()
Corey D
A: 

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.

Eli Courtwright
they are passed by sharing and not by reference, value: http://effbot.org/zone/call-by-object.htm.
Joschua
+10  A: 

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:
            __instance = type.__new__(cls)
            __instance.name = "The one"
        return __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;-).

Alex Martelli
Eheh, SO is sponsoring his (great) creature here :) I agree, of course
AlberT
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 :)
JonahSan
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!-)
Alex Martelli
A: 
self = _instance

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

truppo
+5  A: 

http://stackoverflow.com/questions/1296311/bruce-eckels-code-snippet-from-design-pattern-im-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>
'''
Dustin Getz
+1 for this reference. In particular, see this answer: http://stackoverflow.com/questions/1296311/bruce-eckels-code-snippet-from-design-pattern-im-confused-on-how-it-works/1297577#1297577
Daniel Pryden