Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make isinstance(obj, cls) work with a decorated class

I have a few classes that need to do the following:

When the constructor is called, if an equal object (aka an object with the same id) already exists, return that object. Otherwise, create a new instance. Basically,

>>> cls(id=1) is cls(id=1)
True

To achieve this, I've written a class decorator like so:

class Singleton(object):
    def __init__(self, cls):
        self.__dict__.update({'instances': {},
                                'cls': cls})

    def __call__(self, id, *args, **kwargs):
        try:
            return self.instances[id]
        except KeyError:
            instance= self.cls(id, *args, **kwargs)
            self.instances[id]= instance
            return instance

    def __getattr__(self, attr):
        return getattr(self.cls, attr)
    def __setattr__(self, attr, value):
        setattr(self.cls, attr, value)

This does what I want, but:

@Singleton
class c(object):
    def __init__(self, id):
        self.id= id

o= c(1)
isinstance(o, c) # returns False

How can I fix this? I found a related question, but I just can't seem to adapt those solutions to my use case.


I know someone's gonna ask me to post some code that doesn't work, so here you go:

def Singleton(cls):
    instances= {}
    class single(cls):
        def __new__(self, id, *args, **kwargs):
            try:
                return instances[id]
            except KeyError:
                instance= cls(id, *args, **kwargs)
                instances[id]= instance
                return instance
    return single
# problem: isinstance(c(1), c) -> False

def Singleton(cls):
    instances= {}
    def call(id, *args, **kwargs):
        try:
            return instances[id]
        except KeyError:
            instance= cls(id, *args, **kwargs)
            instances[id]= instance
            return instance
    return call
# problem: isinstance(c(1), c) -> TypeError
like image 607
Aran-Fey Avatar asked Oct 31 '14 20:10

Aran-Fey


1 Answers

You can add your custom __instancecheck__ hook in your decorator class:

def __instancecheck__(self, other):
    return isinstance(other, self.cls)
like image 96
Ashwini Chaudhary Avatar answered Nov 15 '22 12:11

Ashwini Chaudhary