Search code examples
pythonclassoopinstance

Creating unique instance works, but how to get the reference when calling the class again?


I am trying to create a class that stores all created instances in the class itself by using new and init magical methods together and I want to return an existing class if it was created before.

My current code looks like this:

class Obj():
_list = []

def __new__(cls, id, *args, **kwargs):
    print('new')
    for o in Obj._list:
        if o.id == id:
            print('existent')
            return            # 'return o' DOES NOT WORK!
    instance = super(Obj, cls).__new__(cls, *args, **kwargs)
    return instance

def __init__(self, id, *args, **kwargs):
    print('init')
    super().__init__(*args, **kwargs)
    self.id = id
    Obj._list.append(self)

Obj(1)
Obj(1)

The code works as it does not produce doubles in the Obj._list (second call of Obj(1) prints 'existent' and cancels the creation of another instance). But when I change the line return in the new-function to return o then two instances are created though I want a pointer / the name of the existent instance to be returned and not the creation of another instance.

Any chance to achieve this behaviour with this code or any other solution? I am trying to avoid a function inside the class like:

def get(id):
    for i in Obj._list:
        if i.id == id:
            return i

Obj.get(1).my_method_call()

Any help is appreciated! Many thanks in advance!

Ulrich


Solution

  • Here's a modified version of an answer I gave to a similar question:

    def singleton_id(cls):
        instances={}
        def getinstance(id, *args, **kwargs):
            key = "{}__{}".format(cls, id)
            if key not in instances:
                instances[key] = cls(id, *args, **kwargs)
            return instances[key]
        return getinstance
    
    @singleton_id
    class Obj():
        def __init__(self, id, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.id = id
    
    print(id(Obj(1)) == id(Obj(1))) # True
    print(id(Obj(1)) == id(Obj(2))) # False
    

    As you can see by the output of the print statements, the objects created with the same ID are identical.


    Since you want to access the already created instances, I've modified the answer even more.

    Note: I've removed the cls from the key, so this will no longer work as a decorator for different classes with the same key, but this seems to be no requirement in your case.

    def singleton_id(cls):
        def getinstance(id, *args, **kwargs):
            if id not in singleton_id.instances:
                singleton_id.instances[id] = cls(id, *args, **kwargs)
            return singleton_id.instances[id]
        return getinstance
    singleton_id.instances = {}
    
    @singleton_id
    class Obj():
        def __init__(self, id, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.id = id
    
    print(id(Obj(1)) == id(Obj(1))) # True
    print(id(Obj(1)) == id(Obj(2))) # False
    print(singleton_id.instances[2].id) # 2