0

Please consider the following code

class A(object):
    cache = {}

    @classmethod
    def instance_by_id(cls, id_):
        cache = cls.cache
        if id_ in cache:
            print(f"id {id_} already in cache")
        instance = cache.get(id_, A(id_))
        cache[id_] = instance

    def __init__(self, id_):
        self._id = id_
        print(f"creating instance for {id_}")



def main():
    for n in [1, 1, 1, 1]:
        instance = A.instance_by_id(n)


if __name__ == "__main__":
    main()

Its output is

creating instance for 1
id 1 already in cache
creating instance for 1
id 1 already in cache
creating instance for 1
id 1 already in cache
creating instance for 1

I expected the output to be

creating instance for 1
id 1 already in cache
id 1 already in cache
id 1 already in cache

What's going on here?

Gulzar
  • 23,452
  • 27
  • 113
  • 201
  • 1
    In the line `cache.get(id_, A(id_))`, `A(id_)` is evaluated **first**, only then `cache.get` is being called with `id_` and the already created instance as arguments. – DeepSpace Apr 07 '22 at 10:52
  • You can try to use `lru_cache` as a class decorator and avoid all that logic. From a quick test I did it seems to behave as expected – DeepSpace Apr 07 '22 at 10:57
  • @DeepSpace Thanks! the cache decorator is REALLY nice, good to know! Is there a parameterized version of `cached_property`? – Gulzar Apr 07 '22 at 11:21
  • Mmm I guess just `@lru_cache` on methods. – Gulzar Apr 07 '22 at 11:34
  • @DeepSpace Looks like doing `@lru_cache` on a class makes `@classmethod`s not callable, as it [interferes with binding order](https://stackoverflow.com/q/11058686/913098). Also, I couldn't find an easy on-disk caching, which I can do quite easily using above method with pickle. Good to know nontheless – Gulzar Apr 07 '22 at 15:56

0 Answers0