Use `super` function into `partialmethod`

Viewed 78

I'm trying to use the magic python partialmethod functool. But this partial method must referenced a function from my parent class. Consider the test code below :

from functools import partialmethod

class MyResource:
    @staticmethod
    def load_resource(id):
        print(f"Loding a new resource ! {id}")
        return MyResource(id)


class CachedDataMixin:
    _cache = {}
    def _get_resource(self, source_cls, id):
        if source_cls not in self._cache or id not in self._cache[source_cls]:
            resource = source_cls.load_resource(id)
            self._cache.setdefault(source_cls, {})[id] = resource
        return self._cache[source_cls][id]

class MyClass(CachedDataMixin):
   _get_my_resource = partialmethod(_get_resource, MyResource)
   def run(self):
       obj1 = _get_my_resource(12345)
       obj2 = _get_my_resource(12345)
       return obj1, obj2

MyClass().run()

When I tried to run this code, I get an error message NameError: name '_get_resource' is not defined on _get_my_resource = partialmethod(_get_resource, MyResource).

I tried use partialmethod(self._get_resource, MyResource) or partialmethod(super()._get_resource, MyResource) but it doesn't work.

I found a workaround redeclaring the _get_resource function into MyClass but this solution seems very ugly for me :

class MyClass(CachedDataMixin):
    def _wrapped_get_resource(self, source_cls, id):
        return super()._get_resource(source_cls, id)
   _get_my_resource = partialmethod(_wrapped_get_resource, MyResource)
   ...

Is anyone has a pretty solution to not write my ugly workaround ? Many thanks for your help

1 Answers

There's several things here:

  1. You can't reference CachedDataMixin._get_resource in class attribute, because it's not defined within class itself. Instances of MyClass will have it, but class objects differ from instance objects.
  2. You can't use self for the same reason: it's defined only within methods, and refers to an instance object.
  3. super() works dynamically when called from methods (and uses instances), so no luck with using it in class attribute either.
  4. You can hardcode class name here:
    _get_my_resource = partialmethod(CachedDataMixin._get_resource, MyResource)
    
    I would personally go with more explicit option:
    def _get_my_resource(self, id_):
        return super()._get_resource(MyResource, id_)
    
  5. Then, after a couple of fixes like these:
    # you forgot to call a method using `self`
    obj1 = self._get_my_resource(12345)
    
    and
    class MyResource:
        # otherwise you can't do `MyResource(12345)`
        def __init__(self, id_):
            self.id = id_
    

It has finally worked ("loading resource" only once with no errors).

Related