pythonpython-3.xabc

How to create an abstract cached property in Python?


In order to create an abstract property in Python one can use the following code:

from abc import ABC, abstractmethod

class AbstractClassName(ABC):

    @cached_property
    @abstractmethod
    def property_name(self) -> str:
        pass


class ClassName(AbstractClassName):

    @property
    def property_name(self) -> str:
        return 'XYZ'


>>> o = AbstractClassName()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class AbstractClassName with abstract method property_name
>>> o = ClassName()
>>> o.property_name
'XYZ'

which is what I have expected. I wanted to create an abstract cached property, so I tried the following:

from abc import ABC, abstractmethod
from functools import cached_property


class AbstractClassName(ABC):

    @cached_property
    @abstractmethod
    def property_name(self) -> str:
        pass


class ClassName(AbstractClassName):

    @cached_property
    def property_name(self) -> str:
        return 'XYZ'

However, this is not working as I expected:

>>> o = AbstractClassName()
>>> o.property_name
>>> o = ClassName()
>>> o.property_name
'XYZ'

Notice that this time it is allowing me to create an instance of an abstract class AbstractClassName. I am using Python 3.10. Is there any way to defined an abstract cached property?


Solution

  • Here is a possible solution

    from abc import ABC, abstractmethod
    from functools import cached_property
    
    
    class AbstractClassName(ABC):
        @cached_property
        def property_name(self) -> str:
            return self._property_name()
    
        @abstractmethod
        def _property_name(self) -> str:
            ...
    
    
    class ClassName(AbstractClassName):
        def _property_name(self) -> str:
            print("Heavy calculation")
            return "XYZ"
    

    Test

    AbstractClassName()
    # raise
    
    a = ClassName()
    print(a.property_name)
    print(a.property_name)
    # Heavy calculation
    # XYZ
    # XYZ