Hi,
I've been looking into ways of creating singleton objects. With Python2.3 I
usually used a module-level variable and a factory function to implement
singleton objects.
With Python2.4 I was looking into decorators. The examples from PEP 318
http://www.python.org/peps/pep-0318.html#examples
don't work - AFAIK because:
- you cannot decorate class definitions (why was it left out?)
- __init__ must return None
However, you can use the decorator:
def singleton(f):
instances = {}
def new_f(*args, **kwargs):
if (f not in instances):
instances[f] = f(*args, **kwargs)
return instances[f]
new_f.func_name = f.func_name
new_f.func_doc = f.func_doc
return new_f
with a class that overwrites the __new__ methof of new-style classes:
class Foobar(object):
def __init__(self):
print self
@singleton
def __new__(self):
return object.__new__(Foobar)
Is this particularly ugly or bad?
Thanks for comments,
I thought of a different approach. The singleton function here
takes arguments for the initial instance of a class, and instantiates it,
and then modifies the class's __new__ and __init__ to return the initial
instance and prevent further initialization.
Used directly, as normal function decorators can be (i.e., fun = deco(fun)
or fun = deco(args)(fun)), singleton works (not counting bugs ;-) as is.
To invoke it as decorator demands a trick workaround using an intermediate
workaround decorator and a dummy funcion definition synonymous with the
class to be decorated (which mus pre-exist for this).
BTW, this is different from the class-decorator-as-sugar-for-metaclass,
(which I suggested previously) since it take one class argument vs arguments
for the metaclass __new__ etc.
I added a singularize decorator for when you don't immediately want to
specify the first initiazization parameters, but want the first instance
to to become the singleton instance whenever that happens later.
So here's some hacks to play with:
----< singleton.py >-----------------------------------------------
def singleton(*args, **kw):
"""decorator cacheing singleton instance immediately"""
def singdeco(cls):
assert isinstance(cls, type)
inst = cls(*args, **kw)
cls.__new__ = staticmethod(lambda *a, **kw: inst)
cls.__init__ = staticmethod(lambda *a, **kw: None) # no re-init
return cls
return singdeco
import sys
def workaround(f):
cls = sys._getframe(1).f_locals.get(f.func_name)
if cls is None:
cls = sys._getframe(1).f_globals.get(f.func_name)
return cls
def singularize(cls):
"""decorator setting singleton-making mousetrap"""
oldnew = cls.__new__
def __new__(cls, *args, **kw):
inst = oldnew(cls)
inst.__init__(*args, **kw)
cls.__new__ = staticmethod(lambda *a,**kw: inst)
cls.__init__ = staticmethod(lambda *a, **kw: None)
return inst
cls.__new__ = staticmethod(__new__)
return cls
def test():
class Foobar(object): pass
Foobar = singleton()(Foobar)
print Foobar
print [Foobar() for x in xrange(2)]
class Boobar(object):
def __init__(self, v):
print self, v
self.v = v
@singleton(123)
@workaround
def Boobar(): pass # dummy used by workaround for name
print Boobar
print [Boobar(x) for x in xrange(2)]
print [Boobar(x).v for x in xrange(2)]
class Gee(object):
def __init__(self): print 'Gee:', self
Gee = singularize(Gee)
print Gee
print [Gee() for x in xrange(2)]
class Haw(object):
def __init__(self, *args, **kw):
self.args = args
self.kw =kw
Haw = singularize(Haw)
haw = Haw(1,2L, 3.5, s='string', more='example')
print vars(haw)
print vars(Haw(111, a='222'))
print [Haw(), Haw()]
print [Haw().args
for i in xrange(len(Haw().args))]
print [Haw().kw['s'], Haw().kw['more']]
if __name__ == '__main__':
test()
-------------------------------------------------------------------
No guarantees, mind ;-)
Output, showing single instances and no repeat initialization:
<class '__main__.Foobar'>
[<__main__.Foobar object at 0x02F03BEC>, <__main__.Foobar object at 0x02F03BEC>]
<__main__.Boobar object at 0x02F03DCC> 123
<class '__main__.Boobar'>
[<__main__.Boobar object at 0x02F03DCC>, <__main__.Boobar object at 0x02F03DCC>]
[123, 123]
<class '__main__.Gee'>
Gee: <__main__.Gee object at 0x02F03E8C>
[<__main__.Gee object at 0x02F03E8C>, <__main__.Gee object at 0x02F03E8C>]
{'args': (1, 2L, 3.5), 'kw': {'s': 'string', 'more': 'example'}}
{'args': (1, 2L, 3.5), 'kw': {'s': 'string', 'more': 'example'}}
[<__main__.Haw object at 0x02F03F0C>, <__main__.Haw object at 0x02F03F0C>]
[1, 2L, 3.5]
['string', 'example']
Regards,
Bengt Richter