yair koskas
yair koskas

Reputation: 144

Creating a singleton for each instance of a class

I have a python class, let's call it C. it takes a string in it's constructor. how could i make sure (design-wise) that

x = C('a')
y = C('b')
z = C('a')

this configuration leads to x and y pointing to different places, and x and z pointing to the same place? meaning

x is y == False
x is z == True

Upvotes: 2

Views: 394

Answers (2)

tobias_k
tobias_k

Reputation: 82899

One lazy way to do this would be to annotate the class itself with functools.lru_cache:

from functools import lru_cache

@lru_cache(None)
class C:
    def __init__(self, val):
        self.val = val

x = C('a')
y = C('b')
z = C('a')

print(x is y)  # False
print(x is z)  # True

lru_cache is actually intended for caching functions, but it works with any callable, including classes, where it caches the call to C.__init__. Parameters must be hashable, of course.

This way, as far as I know, instances are not garbage-collected even if they are no longer used, but you could control the maximum number of instances to cache with the parameter to lru_cache (None meaning "no limit"), which might be good enough for many cases.

Upvotes: 1

Iain Shelvington
Iain Shelvington

Reputation: 32244

You can use a WeakValueDictionary to keep all existing instances of your class in a dict like object on the class. In the __new__ method you look up the existing object in the dict and if it exists return it, if not create a new instance and save it to the dict

from weakref import WeakValueDictionary


class C:

    __instances = WeakValueDictionary()

    def __new__(cls, value):
        obj = cls.__instances.get(value)
        if not obj:
            obj = object.__new__(cls)
            cls.__instances[value] = obj
        return obj

    def __init__(self, value):
        self.value = value

We use weak references to the instances so that they can be garbage collected when nothing else references them.

Upvotes: 2

Related Questions