Python – Why does isinstance() return False in this case?

Why does isinstance() return False in this case?… here is a solution to the problem.

Why does isinstance() return False in this case?

I want to write a decorator that allows memory constructors. When I construct a class, I want to return objects from the cache whenever possible.

The following code is adapted from here

from functools import wraps

def cachedClass(klass):
    cache = {}

@wraps(klass, updated=())
    class wrapper:
        def __new__(cls, *args, **kwargs):
            key = (cls,) + args + tuple(kwargs.items())
            try:
                inst = cache.get(key, None)
            except TypeError:
                # Can't cache this set of arguments
                inst = key = None
            if inst is None:
                inst = klass.__new__(klass, *args, **kwargs)
                inst.__init__(*args, **kwargs)
                if key is not None:
                    cache[key] = inst
            return inst

return wrapper

A small test suite reevaluates the following:

>>> @cachedClass
... class Foo:
...     pass
>>> f1 = Foo()
>>> f2 = Foo()
>>> f1 is f2
True
>>> Foo
<class 'cache. Foo'>
>>> type(f1)
<class 'cache. Foo'>
>>> isinstance(f1, Foo)
False

I expect the last expression to return True. What am I missing?

Solution

@cachedClass
class Foo:
    pass

Semantically equivalent to

class Foo:
    pass

Foo = cachedClass(Foo)

The Foo name is given the return value of cachedClass, which is
Wrapper class.

Wrapper is decorated by functool.wraps, which duplicates Foo's __name__ attribute and some other dunder attributes, so the wrapper class looks like Foo.

If you delete @wraps line and print it

print(type(f1), Foo, type(f1) is Foo, sep=', ')

You’ll see that f1 is an instance of the original Foo class, but the Foo name now refers to the wrapper class, which are different objects:

<class '__main__. Foo'>, <class '__main__.cachedClass.<locals>.wrapper'>, False

Related Problems and Solutions