singleton objects with decorators

Bengt Richter bokr at oz.net
Tue Apr 12 19:40:10 EDT 2005


On 12 Apr 2005 08:00:42 -0700, "Michele Simionato" <michele.simionato at gmail.com> wrote:

>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



More information about the Python-list mailing list