How to create a limited set of instanceses of a class

Alex Martelli aleax at mac.com
Sat Jul 1 13:22:06 EDT 2006


madpython <madpython at gmail.com> wrote:

> For the pure theory sake and mind expansion i got a question.
> Experimenting with __new__ i found out how to create a singleton.
> class SingleStr(object):
>     def __new__(cls,*args,**kwargs):
>         instance=cls.__dict__.get('instance')
>         if instance:
>             return instance
>         cls.instance=object.__new__(cls,*args,**kwargs)
>         return cls.instance
> 
> What if i need to create no more than 5 (for example) instances of a

And what if somebody asks for a 6th one -- raise an exception?  or
return a random one of the 5?  Ah well, anyway...:

> class. I guess that would be not much of a problema but there is a
> complication. If one of the instances is garbagecollected I want to be
> able to spawn another instance. Maybe it's difficult to perceive what i
> said so think of a limited number of sockets. Wherenever one is freed
> it can be captured again.

Have the class hold weak references to the instances.  E.g.:

import weakref
import random

class JustFive(object):
    _counter = 0
    _insts = weakref.WeakKeyDictionary()

    def __new__(cls):
        if len(cls._insts) < 5:
            inst = object.__new__(cls)
            inst.id = cls._counter
            cls._insts[inst] = None
            cls._counter += 1
        else:
            inst = random.choice(cls._insts.keys())
        return inst

Testing this for the specified behavior should be done more
systematically, of course, but here's a snippet based only on showing
exactly what's going on...:


if __name__ == '__main__':
    samp = [JustFive() for i in range(7)]
    def showsamp():
        for s in samp: print s.id,
        ss = set(s.id for s in samp)
        print '{',
        for s in sorted(ss): print '%d,'%s,
        print '} #%d'%len(ss)
    print 'Start with:',
    showsamp()
    for x in range(10):
        i = random.randrange(len(samp))
        print ('@%d,'%i), samp[i].id, '->',
        samp[i] = None
        newone = JustFive()
        samp[i] = newone
        print '%d,' % samp[i].id,
        showsamp()


A sample run might go, for example...:

Start with: 0 1 2 3 4 4 1 { 0, 1, 2, 3, 4, } #5
@0, 0 -> 5, 5 1 2 3 4 4 1 { 1, 2, 3, 4, 5, } #5
@1, 1 -> 3, 5 3 2 3 4 4 1 { 1, 2, 3, 4, 5, } #5
@6, 1 -> 6, 5 3 2 3 4 4 6 { 2, 3, 4, 5, 6, } #5
@2, 2 -> 7, 5 3 7 3 4 4 6 { 3, 4, 5, 6, 7, } #5
@4, 4 -> 7, 5 3 7 3 7 4 6 { 3, 4, 5, 6, 7, } #5
@4, 7 -> 6, 5 3 7 3 6 4 6 { 3, 4, 5, 6, 7, } #5
@1, 3 -> 7, 5 7 7 3 6 4 6 { 3, 4, 5, 6, 7, } #5
@3, 3 -> 8, 5 7 7 8 6 4 6 { 4, 5, 6, 7, 8, } #5
@6, 6 -> 5, 5 7 7 8 6 4 5 { 4, 5, 6, 7, 8, } #5
@2, 7 -> 6, 5 7 6 8 6 4 5 { 4, 5, 6, 7, 8, } #5

and the point is that the sample always has a set of objects with
exactly 5 separate id values, but the set "migrates" upwards as some
objects happen to be dropped.  Well, not always "upwards" as smoothly as
luck would have it in this case, e.g., another run goes:

Start with: 0 1 2 3 4 1 2 { 0, 1, 2, 3, 4, } #5
@1, 1 -> 3, 0 3 2 3 4 1 2 { 0, 1, 2, 3, 4, } #5
@2, 2 -> 4, 0 3 4 3 4 1 2 { 0, 1, 2, 3, 4, } #5
@3, 3 -> 1, 0 3 4 1 4 1 2 { 0, 1, 2, 3, 4, } #5
@1, 3 -> 5, 0 5 4 1 4 1 2 { 0, 1, 2, 4, 5, } #5
@5, 1 -> 5, 0 5 4 1 4 5 2 { 0, 1, 2, 4, 5, } #5
@6, 2 -> 6, 0 5 4 1 4 5 6 { 0, 1, 4, 5, 6, } #5
@2, 4 -> 1, 0 5 1 1 4 5 6 { 0, 1, 4, 5, 6, } #5
@4, 4 -> 7, 0 5 1 1 7 5 6 { 0, 1, 5, 6, 7, } #5
@6, 6 -> 8, 0 5 1 1 7 5 8 { 0, 1, 5, 7, 8, } #5
@3, 1 -> 8, 0 5 1 8 7 5 8 { 0, 1, 5, 7, 8, } #5

here, the objects with ids 0 and 1 just never happen to get dropped.


Alex



More information about the Python-list mailing list