holoviz / holoviz/param

Make param.depends efficient

Open
#927 6 comments 0 reactions 0 assignees View on GitHub
type-feature
Dominant language
Python
Stars
521
Forks
86
Avg merge
1d 13h
Merged PRs (30d)
35

Description

Its becoming more and more clear to me just how inefficient param.depends and param.bind are if you want to depend on them multiple times.

Thus I suggested at HoloViz meeting that for Panel that we start recommending `pn.rx(some_func)(args,...)` over `pn.bind(some_func, args...)` because the end usage is the same. But the pn.rx version is much more efficient. But the feedback was that `pn.rx` was not yet well enough understood.

As an alternative I would suggest making `pn.depends` (and `pn.bind`) more efficient by caching results similar to what `pn.rx` does.

One more argument is that Panel tutorials promotes creating more advanced applications with the DataStore design pattern. And this also promotes depending multiple times on references. The tutorial uses `pn.rx`. I don't know if Philipp did it for efficiency reasons or just because he could? But using `param.depends` without `watch=True` would have led to an inefficient application https://panel.holoviz.org/tutorials/intermediate/structure_data_store.html.

## Example

The below example shows how inefficient it is to depend on a `param.depends` annotated function multiple times.

https://github.com/holoviz/param/assets/42288570/c814b45f-d3e7-419f-8392-6c2e89930c4e

```python
import param
import panel as pn

pn.extension()

slider = pn.widgets.IntSlider(value=0, start=1, end=2)

class Model(param.Parameterized):
value = param.Integer(default=1, bounds=(1,10))

count = param.Integer(default=0)

@param.depends("value")
def result(self):
self.count+=1
return self.value

@param.depends("result")
def other_result(self):
return self.result()

class Model2(param.Parameterized):
value = param.Integer(default=1, bounds=(1,10))
result = param.Integer()
other_result = param.Integer()

count = param.Integer(default=0)

@param.depends("value", watch=True)
def _update_result(self):
self.count+=1
self.result = self.value

@param.depends("result", watch=True)
def _update_other_result(self):
self.other_result= self.result

model = Model()
model2 = Model2()

pn.Row(
pn.Column(model.param.value, model.param.count, model.result, model.other_result),
pn.Column(model2.param.value, model2.param.count, pn.pane.Str(model2.param.result), pn.pane.Str(model2.param.other_result))
).servable()
```

As you can see, you can avoid the inefficiency by using `watch=True` and updating named parameters. But this makes your code much longer and much more complicated.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.