2

While I was watching some code about singleton's in python I decided to write my own.

This was my first code:

def singleton(cls):
    instance = False
    def constructor(*args,**kwargs):
        if not instance:
            instance = cls(*args,**kwargs)
        return instance
    return constructor

But when i tested it, the interpreter told me that 'instance' must be declared before being used on the if condition, finally I figured out to do it as follows:

def singleton(cls):
    cls._instance = False
    def constructor(*args,**kwargs):
        if not cls._instance:
            cls._instance = cls(*args,**kwargs)
        return cls._instance
    return constructor

and it worked as expected:

>>> @singleton
>>> class A: pass
>>> a=A()
>>> id(a)
33479456
>>> b=A()
>>> id(b)
33479456

Why the closure from the first example didn't work.

Edit: the error was

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "singleton.py", line 4, in constructor
    if not instance:
UnboundLocalError: local variable 'instance' referenced before assignment
loki
  • 1,702
  • 4
  • 25
  • 37
  • 2
    See also http://www.python.org/dev/peps/pep-0318/#examples and http://stackoverflow.com/questions/31875/is-there-a-simple-elegant-way-to-define-singletons-in-python – James Youngman Apr 14 '12 at 23:28

2 Answers2

3

Your first closure didn't work because inside your constructor function, you assigned to instance. That makes instance a local name inside constructor, and you accessed that local name before it was assigned to.

In Python 3, you can use nonlocal instance to declare the scope of instance. In Python 2, you can't access that outer instance name as you want to.

Also, singletons are unusual in Python. Why not just instantiate your class once? Why try to trick Python into behaving differently than it does? Or create a factory function to produce the instance to use?

Ned Batchelder
  • 323,515
  • 67
  • 518
  • 625
0

Closure-variables are read-only in Python, so when you attempt to assing a value to instance, Python can't do it. The error message is slightly misleading, because instance is declared, it just isn't writeable inside the closure.

AHM
  • 4,737
  • 30
  • 36
  • Ah, well, just saw your update. Turns out the error message wasn't that misleading at all :-) – AHM Apr 14 '12 at 23:20
  • 2
    closure variables are not read-only, they're just awkward to modify, as the OP has found out. – Ned Batchelder Apr 14 '12 at 23:25
  • I actually thought they were impossible to modify? In Python 2, is it possible in some awkward way to assign a new value to a closure variable? – AHM Apr 14 '12 at 23:29
  • 1
    @AHM You're conflating modifying an object and pointing a name. He can do `instance = []` in the non-local scope, then `instance.append(1)` in the local scope, modifying it, without any issue. It's actually pointing the name at another object that is different. – agf Apr 14 '12 at 23:34
  • Ah. okay, yes. I was aware of that, just not formulating myself correctly there. For a moment I thought there were some mysterious way of getting functionality like the nonlocal keyword from Python 3 in Python 2. – AHM Apr 14 '12 at 23:37
  • @NedBatchelder Your reputation is so high. You create a python version hyphenate, would you take a minute and have a look at stackoverflow.com/questions/10159414/… We do not know how to create a pattern file for syllabification by using hyphenation. We know that French hyphenation pattern is built on syllables. How to make a pattern file that suits english syllabification. – Warren 36 m – Warren Apr 16 '12 at 14:17