Singleton-like pattern

Discussion in 'Python' started by Pedro Werneck, Aug 1, 2003.

  1. I need to implement an object that behaves just like imutable objects,
    like int, str and tuple: if you create two objects with the same value
    at the same time, you get the same object, like a singleton, but
    instead of storing a unique object, store a set of objects, with no
    duplicates. I don't know if this is a common pattern (and if there's a
    name for it), but I remember reading something about it some time
    ago... the ASPN cookbook has some similar patterns, but none works for
    me...

    I know there are several ways to implement this, but I think this
    metaclass approach is more elegant than using __new__ and
    inheritance... at least it solves my problem (I'm working on a card
    game) but I am sure that there are better ways to do it... I could not
    find any other value suitable as a key and using str(args) + str(kwds)
    is ugly and easy to break...


    class MultiSingleton(type):
    def __call__(cls, *args, **kwds):
    cache = cls.__dict__.get('__cache__')
    if cache is None:
    cls.__cache__ = cache = {}
    tag = str(args) + str(kwds)
    if tag in cache:
    return cache[tag]
    obj = object.__new__(cls)
    obj.__init__(*args, **kwds)
    cache[tag] = obj
    return obj


    Any help will be much appreciated... thanks in advance

    Pedro Werneck
     
    Pedro Werneck, Aug 1, 2003
    #1
    1. Advertising

  2. (Pedro Werneck) wrote in message news:<>...
    > I need to implement an object that behaves just like imutable objects,
    > like int, str and tuple: if you create two objects with the same value
    > at the same time, you get the same object, like a singleton, but
    > instead of storing a unique object, store a set of objects, with no
    > duplicates. I don't know if this is a common pattern (and if there's a
    > name for it), but I remember reading something about it some time
    > ago... the ASPN cookbook has some similar patterns, but none works for
    > me...
    >
    > I know there are several ways to implement this, but I think this
    > metaclass approach is more elegant than using __new__ and
    > inheritance... at least it solves my problem (I'm working on a card
    > game) but I am sure that there are better ways to do it... I could not
    > find any other value suitable as a key and using str(args) + str(kwds)
    > is ugly and easy to break...
    >
    >
    > class MultiSingleton(type):
    > def __call__(cls, *args, **kwds):
    > cache = cls.__dict__.get('__cache__')
    > if cache is None:
    > cls.__cache__ = cache = {}
    > tag = str(args) + str(kwds)
    > if tag in cache:
    > return cache[tag]
    > obj = object.__new__(cls)
    > obj.__init__(*args, **kwds)
    > cache[tag] = obj
    > return obj
    >
    >
    > Any help will be much appreciated... thanks in advance
    >
    > Pedro Werneck


    "memoize" is a possible name for this. Notice that the metaclass is a
    bit of overkill, you may well use a simple function for this job.
    About the issue of finding a suitable key, in the same situation I have
    used the tuple (args,kw) as key. But me too I would like to ask if this is a
    good idea. What's the custom solution for getting a good key from
    a dictionary ?


    Michele
     
    Michele Simionato, Aug 1, 2003
    #2
    1. Advertising

  3. On 1 Aug 2003 09:15:34 -0700, (Michele Simionato) wrote:

    > (Pedro Werneck) wrote in message news:<>...
    >> I need to implement an object that behaves just like imutable objects,
    >> like int, str and tuple: if you create two objects with the same value
    >> at the same time, you get the same object, like a singleton, but
    >> instead of storing a unique object, store a set of objects, with no
    >> duplicates. I don't know if this is a common pattern (and if there's a
    >> name for it), but I remember reading something about it some time
    >> ago... the ASPN cookbook has some similar patterns, but none works for
    >> me...
    >>
    >> I know there are several ways to implement this, but I think this
    >> metaclass approach is more elegant than using __new__ and
    >> inheritance... at least it solves my problem (I'm working on a card
    >> game) but I am sure that there are better ways to do it... I could not
    >> find any other value suitable as a key and using str(args) + str(kwds)
    >> is ugly and easy to break...
    >>
    >>
    >> class MultiSingleton(type):

    class MultiSingleton(object):
    >> def __call__(cls, *args, **kwds):

    def __new__(cls, *args, **kwds):
    >> cache = cls.__dict__.get('__cache__')
    >> if cache is None:
    >> cls.__cache__ = cache = {}
    >> tag = str(args) + str(kwds)

    tag = '%r%r'% (args, kwds) # might be an alternative
    >> if tag in cache:
    >> return cache[tag]
    >> obj = object.__new__(cls)
    >> obj.__init__(*args, **kwds)
    >> cache[tag] = obj
    >> return obj
    >>
    >>
    >> Any help will be much appreciated... thanks in advance
    >>
    >> Pedro Werneck

    >
    >"memoize" is a possible name for this. Notice that the metaclass is a
    >bit of overkill, you may well use a simple function for this job.

    Why not just change as above, and sublass to inherit the behavior
    (and create subclass-specific caches) ?

    >About the issue of finding a suitable key, in the same situation I have
    >used the tuple (args,kw) as key. But me too I would like to ask if this is a
    >good idea. What's the custom solution for getting a good key from
    >a dictionary ?

    Does (args,kw) work in general? IWT you could easily get something unhashable?
    IWT using a tuple of actual args may also be a bad idea since it would prevent callers'
    temp args from being garbage collected. I use repr to avoid that, maybe mistakenly?

    Regards,
    Bengt Richter
     
    Bengt Richter, Aug 1, 2003
    #3
  4. Pedro Werneck

    Carl Banks Guest

    Michele Simionato wrote:
    > About the issue of finding a suitable key, in the same situation I have
    > used the tuple (args,kw) as key.


    Surely not? Perhaps you meant tuple(args,tuple(kw.items())) ? It gets
    rid of the unhashable kw dict.


    > But me too I would like to ask if this is a
    > good idea. What's the custom solution for getting a good key from
    > a dictionary ?


    Howabout cPickle.dumps((args,kwargs),1)?


    --
    CARL BANKS
     
    Carl Banks, Aug 1, 2003
    #4
  5. On 1 Aug 2003, Bengt Richter wrote:

    > >> class MultiSingleton(type):

    > class MultiSingleton(object):
    > >> def __call__(cls, *args, **kwds):

    > def __new__(cls, *args, **kwds):
    > >> cache = cls.__dict__.get('__cache__')
    > >> if cache is None:
    > >> cls.__cache__ = cache = {}
    > >> tag = str(args) + str(kwds)

    > tag = '%r%r'% (args, kwds) # might be an alternative
    > >> if tag in cache:
    > >> return cache[tag]
    > >> obj = object.__new__(cls)
    > >> obj.__init__(*args, **kwds)
    > >> cache[tag] = obj
    > >> return obj


    This is exactly what I did at first... I only changed it to a metaclass
    because I was adding it to a module that already contain some other
    metaclasses I use; I think it's more elegant as I said before... a
    'quick and dirty' benchmark with the timeit module returned the
    folowing results, and in this project I will be dealing with a database
    of 7000+ items, this 20% bit may help a little:

    __metaclass__:
    [39.744094967842102, 40.455733060836792, 42.027853965759277]

    __new__:
    [47.914013981819153, 48.721022009849548, 49.430392026901245]


    On 1 Aug 2003, Michele Simionato wrote:

    > "memoize" is a possible name for this. Notice that the metaclass is a
    > bit of overkill, you may well use a simple function for this job.


    Hey... you wrote such a good article on python metaclasses and now don't
    want people to use them ? :)

    > Does (args,kw) work in general? IWT you could easily get something unhashable?
    > IWT using a tuple of actual args may also be a bad idea since it would prevent callers'
    > temp args from being garbage collected. I use repr to avoid that, maybe mistakenly?


    > About the issue of finding a suitable key, in the same situation I have
    > used the tuple (args,kw) as key. But me too I would like to ask if this is a
    > good idea. What's the custom solution for getting a good key from
    > a dictionary ?


    Actually, (args, kw) doesn't work at all, even if you only get hashable
    arguments... the 'kw' dict invalidate it as a key... besides, there's the
    garbage collection problem, as Bengt Richter mentioned...

    The "%r%r"%(args, kwds) works, as well as str((args, kwds))), but it
    breaks if you get as argument an object with the default __repr__, as
    the object id may be different, even if they are equal. Overriding __repr__
    and returning a real representation avoids this problem.

    Thanks for your time.

    Pedro Werneck
     
    Pedro Werneck, Aug 2, 2003
    #5
  6. (Pedro Werneck) wrote in message news:<>...
    > On 1 Aug 2003, Bengt Richter wrote:
    >
    > > >> class MultiSingleton(type):

    > class MultiSingleton(object):
    > > >> def __call__(cls, *args, **kwds):

    > def __new__(cls, *args, **kwds):
    > > >> cache = cls.__dict__.get('__cache__')
    > > >> if cache is None:
    > > >> cls.__cache__ = cache = {}
    > > >> tag = str(args) + str(kwds)

    > tag = '%r%r'% (args, kwds) # might be an alternative
    > > >> if tag in cache:
    > > >> return cache[tag]
    > > >> obj = object.__new__(cls)
    > > >> obj.__init__(*args, **kwds)
    > > >> cache[tag] = obj
    > > >> return obj

    >
    > This is exactly what I did at first... I only changed it to a metaclass
    > because I was adding it to a module that already contain some other
    > metaclasses I use; I think it's more elegant as I said before... a
    > 'quick and dirty' benchmark with the timeit module returned the
    > folowing results, and in this project I will be dealing with a database
    > of 7000+ items, this 20% bit may help a little:
    >
    > __metaclass__:
    > [39.744094967842102, 40.455733060836792, 42.027853965759277]
    >
    > __new__:
    > [47.914013981819153, 48.721022009849548, 49.430392026901245]
    >
    >
    > On 1 Aug 2003, Michele Simionato wrote:
    >
    > > "memoize" is a possible name for this. Notice that the metaclass is a
    > > bit of overkill, you may well use a simple function for this job.

    >
    > Hey... you wrote such a good article on python metaclasses and now don't
    > want people to use them ? :)


    Maybe it is because I know them that I don't want to abuse them ;)
    Consider also that not everybody knows about metaclasses, and
    I want my code to be readable to others; finally, there is
    Occam's rasor argument (i.e. do not use metaclasses without necessity).

    > > Does (args,kw) work in general? IWT you could easily get something unhashable?
    > > IWT using a tuple of actual args may also be a bad idea since it would prevent callers'
    > > temp args from being garbage collected. I use repr to avoid that, maybe mistakenly?

    >
    > > About the issue of finding a suitable key, in the same situation I have
    > > used the tuple (args,kw) as key. But me too I would like to ask if this is a
    > > good idea. What's the custom solution for getting a good key from
    > > a dictionary ?

    >
    > Actually, (args, kw) doesn't work at all, even if you only get hashable
    > arguments... the 'kw' dict invalidate it as a key... besides, there's the
    > garbage collection problem, as Bengt Richter mentioned...


    Yes, as I replied to Carl Banks, actually I have got problems with
    (args,kw) and I think at the end I used args+tuple(kw.iteritems()),
    but I had forgotten at the time of the posting.

    > The "%r%r"%(args, kwds) works, as well as str((args, kwds))), but it
    > breaks if you get as argument an object with the default __repr__, as
    > the object id may be different, even if they are equal. Overriding __repr__
    > and returning a real representation avoids this problem.
    >
    > Thanks for your time.
    >
    > Pedro Werneck



    Michele
     
    Michele Simionato, Aug 2, 2003
    #6
  7. Quoth Michele Simionato:
    [...]
    > Actually you are right, I remember now that I got problems with
    > (args,kw) and at the end I used args,tuple(kw.items()). But I was
    > ensure if this was a good solution.


    I'd worry about the order of kw.items().

    --
    Steven Taschuk Aral: "Confusion to the enemy, boy."
    Mark: "Turn-about is fair play, sir."
    -- _Mirror Dance_, Lois McMaster Bujold
     
    Steven Taschuk, Aug 2, 2003
    #7
  8. Steven Taschuk <> wrote in message news:<>...
    > Quoth Michele Simionato:
    > [...]
    > > Actually you are right, I remember now that I got problems with
    > > (args,kw) and at the end I used args,tuple(kw.items()). But I was
    > > ensure if this was a good solution.

    >
    > I'd worry about the order of kw.items().


    Yes, indeed. Also, the args tuple can contain nested dictionaries and
    become unhashable; in such a case I should recursively flatten all
    the dictionaries to tuples, taking in account the ordering.
    To much work for me ...I have really to look at the cPickle solution.
    How does it solve the ordering issue?

    Michele
     
    Michele Simionato, Aug 3, 2003
    #8
  9. Pedro Werneck

    Carl Banks Guest

    Michele Simionato wrote:
    > Steven Taschuk <> wrote in message news:<>...
    >> Quoth Michele Simionato:
    >> [...]
    >> > Actually you are right, I remember now that I got problems with
    >> > (args,kw) and at the end I used args,tuple(kw.items()). But I was
    >> > ensure if this was a good solution.

    >>
    >> I'd worry about the order of kw.items().

    >
    > Yes, indeed. Also, the args tuple can contain nested dictionaries and
    > become unhashable; in such a case I should recursively flatten all
    > the dictionaries to tuples, taking in account the ordering.
    > To much work for me ...I have really to look at the cPickle solution.
    > How does it solve the ordering issue?



    Unfortunately, not well.

    >>> b = { 1: 1, 9: 1 }
    >>> b

    {1: 1, 9: 1}
    >>> c = { 9: 1, 1: 1 }
    >>> c

    {9: 1, 1: 1}
    >>> from cPickle import dumps
    >>> dumps(b)

    '(dp1\nI1\nI1\nsI9\nI1\ns.'
    >>> dumps(c)

    '(dp1\nI9\nI1\nsI1\nI1\ns.'



    --
    CARL BANKS
    "You don't run Microsoft Windows. Microsoft Windows runs you."
     
    Carl Banks, Aug 3, 2003
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Proton Projects - Moin

    Singleton - Whether Cloneable overrides Singleton

    Proton Projects - Moin, Mar 26, 2007, in forum: Java
    Replies:
    4
    Views:
    3,256
    Proton Projects - Moin
    Mar 27, 2007
  2. Wilhelm
    Replies:
    1
    Views:
    167
  3. Trans
    Replies:
    12
    Views:
    280
    Robert Klemme
    Sep 14, 2007
  4. Paul McMahon
    Replies:
    3
    Views:
    206
    David A. Black
    Jun 9, 2008
  5. Charles Oliver Nutter

    Singleton methods without the singleton class

    Charles Oliver Nutter, Mar 15, 2010, in forum: Ruby
    Replies:
    4
    Views:
    204
    Charles Oliver Nutter
    Mar 22, 2010
Loading...

Share This Page