Cached Dynamic Parameter

I’m looking to implement a pull model Dynamic Parameters for an expensive calculation that can return a large array of values. A series of different manipulations of the final array will be completed but it should never change as long as the dependent parameters within the parent class are not changed. It may never be accessed during the user’s analysis so I don’t want the force this calculation unnecessarily. I’m sure the approach I have working is probably a rather hackish approach to solving this problem but I have failed all other attempts. An Example of what I have working is as follows

import numpy as np
import param

class CachedExpensiveArray(param.Parameterized):
    parent = param.Parameter()
    cached_value = param.Parameter(default=None, allow_None=True)
        
    def __call__(self):
        if self.cached_value is None:
            ## Do some really expensive calculation and set the array values
            print("Original Calc completed")
            a = np.array([[self.parent.f1], [self.parent.f2], [self.parent.f3]])
            self.cached_value = np.hstack((a, np.random.random((3,5))))
            return self.cached_value
        else:
            print("Cached Result")
            return self.cached_value
        
    def clear(self):
        self.cached_value = None

class TestClass(param.Parameterized):
    p = param.Dynamic()
    
    f1 = param.Number(1)
    f2 = param.Number(2)
    f3 = param.Number(3)
    
    def __init__(self, **params):
        super(TestClass, self).__init__(**params)
        self.p = CachedExpensiveArray(parent=self)
        
    @param.depends('f1','f2','f3', watch=True)
    def _clear_cached_array(self):
        self.p = CachedExpensiveArray(parent=self)
            
t = TestClass()

for i in range(3):
    print(t.p)

this prints the following

Original Calc completed
[[1.         0.81225398 0.18993548 0.67357551 0.96969163 0.86284378]
 [2.         0.75041076 0.40631964 0.83326135 0.7365204  0.98758376]
 [3.         0.0938844  0.99253526 0.84002868 0.11344561 0.19863643]]
Cached Result
[[1.         0.81225398 0.18993548 0.67357551 0.96969163 0.86284378]
 [2.         0.75041076 0.40631964 0.83326135 0.7365204  0.98758376]
 [3.         0.0938844  0.99253526 0.84002868 0.11344561 0.19863643]]
Cached Result
[[1.         0.81225398 0.18993548 0.67357551 0.96969163 0.86284378]
 [2.         0.75041076 0.40631964 0.83326135 0.7365204  0.98758376]
 [3.         0.0938844  0.99253526 0.84002868 0.11344561 0.19863643]]

if I then change a parameter on the TestClass instance like this

t.f1 = 4
for i in range(3):
    print(t.p)

I get the following

Original Calc completed
[[4.         0.84168804 0.52191216 0.55006135 0.85956553 0.52409848]
 [2.         0.10932542 0.92722704 0.10955687 0.70387541 0.32335225]
 [3.         0.83724005 0.82746697 0.17713918 0.94543894 0.69945296]]
Cached Result
[[4.         0.84168804 0.52191216 0.55006135 0.85956553 0.52409848]
 [2.         0.10932542 0.92722704 0.10955687 0.70387541 0.32335225]
 [3.         0.83724005 0.82746697 0.17713918 0.94543894 0.69945296]]
Cached Result
[[4.         0.84168804 0.52191216 0.55006135 0.85956553 0.52409848]
 [2.         0.10932542 0.92722704 0.10955687 0.70387541 0.32335225]
 [3.         0.83724005 0.82746697 0.17713918 0.94543894 0.69945296]]

so it appears to work.

I have a few questions

  1. I use @param.depends to watch the parameters the calc depends on and then just bulk replace the CachedExpensiveArray object. I’m doing this because all attempts to reset the cached_value on the p param.Dynamic parameter to None from the TestClass have failed and I can’t seem to use the param.watch pattern to connect parameter changes on the TestClass to the clear method on the CachedExpensiveArray instance.

  2. I assume I should probably by subclassing the param.Dynamic class to make a new parameter that does all of this but I have failed at successfully doing this as well.

  3. Is there a way to access the param.owner parameter from the CachedExpensiveArray class rather than passing a parent? It seems to only be available after I instantiate the TestClass which makes sense but it throws an error if I try to access it from within the CachedExpensiveArray class.