I did not put memoize on __new__. I put it on the metaclass __call__.
Here is my memoize:
def memoize(func):
memoize_dic = {}
def wrapped_func(*args):
if args in memoize_dic:
return memoize_dic[args]
else:
result = func(*args)
memoize_dic[args] = result
return result
wrapped_func.__name__ = func.__name__
wrapped_func.__doc__ = func.__doc__
wrapped_func.__dict__ = func.__dict__
return wrapped_func
class Memoize(type): # Singleton is a special case of Memoize
@memoize
def __call__(cls, *args):
return super(Memoize, cls).__call__(*args)
Thanks, that is nice and simple, though caching instances of a class
according to initialization parameters is not quite the same concept
as singleton instances, I think. OTOH, if you want to pass differing
parameters to the same instance of a class, there are lots of methods
(pun ;-) to do that that are clearer than (ab)using the constructor interface.
I.e., what is the difference between shared access to a callable instance
in a module vs shared access to a strange class in the same place?
Hm, just had a thought re memoize: you could give it its own optional
hashing function as a keyword argument, and let it use that as a key
for memoize_dic. Then you could use that to make a singleton(-making) class
or a dual/doubleton-making class like bool, e.g., Bool below
----< memoize.py >--------------------------------------------------------
def memoize(arghash=lambda args:args, method=False):
def _memoize(func):
memoize_dic = {}
def wrapped_func(*args):
key = arghash(args[method:])
if key in memoize_dic:
return memoize_dic[key]
else:
result = func(*args)
memoize_dic[key] = result
return result
wrapped_func.__name__ = func.__name__
wrapped_func.__doc__ = func.__doc__
wrapped_func.__dict__ = func.__dict__
return wrapped_func
return _memoize
def mkPolyton(arghash=lambda args:args):
class Memoize(type): # Singleton is a special case of Memoize
@memoize(arghash, True) # (with arghash=lambda args:0 -> singleton)
def __call__(cls, *args):
return super(Memoize, cls).__call__(*args)
return Memoize
class Bool(int):
__metaclass__ = mkPolyton(lambda args:args and args[0] and 1 or 0)
def __repr__(self): return ('False', 'True')[self]
__str__ = __repr__
def tests(todo):
if '1' in todo:
@memoize()
def square(x): return x*x
print '[id(square(1234))...]: ten have same id:', [id(square(1234))
for x in xrange(10)].count(id(square(1234))) == 10
if '2' in todo:
F = Bool(0) # init cache with proper False value
T = Bool(1) # ditto for True value
print 'T:', T, id(T)
print 'F:', F, id(F)
print '[id(Bool(1..10))...]: ten have same id:', [id(Bool(x))
for x in xrange(1,11)].count(id(T)) == 10
if __name__ == '__main__':
import sys
tests(sys.argv[1:])
--------------------------------------------------------------------------
Result (not exactly a thorough test ;-):
[16:36] C:\pywk\ut>py24 memoize.py 1 2
[id(square(1234))...]: ten have same id: True
T: True 49271436
F: False 49271884
[id(Bool(1..10))...]: ten have same id: True
BTW, I found this page interesting:
http://c2.com/cgi/wiki?SingletonPattern
Regards,
Bengt Richter