get rid of pn.depends
at the same time as recommended in the doc
Oh! Be careful! We didn’t recommend getting rid of param.depends! Could you point us to the place where you saw that? It seems we need to make our suggestions clearer. What we’ve moved to is to suggest using pn.bind
over @pn.depends
to create a bound callable that you pass to Panel.
So we used to decorate functions with @pn.depends
and pass them to Panel like this:
w1 = pn.widgets.FloatSlider()
w2 = pn.widgets.FloatSlider()
@pn.depends(w1, w2)
def add(a, b):
return a + b
pn.Column(w1, w2, add)
Instead, now we prefer suggesting relying on pn.bind
, we find it cleaner! It helps keeping the business logic separate from the user interface code. It’s also easier for people to see that they can import pretty much any function from external libraries and turn them into interactive functions whose input(s) are totally/partially driven by widgets and output is displayed by Panel.
def multiply(a, b):
return a * b
pn.Column(w1, w2, pn.bind(multiply, w1, w2))
(If you know how decorators work, you’ll realize that pn.bind
is just some syntactic sugar over pn.depends
, you could replace pn.bind(multiply, w1, w2)
with pn.depends(w1, w2)(multiply)
.)
Using @param.depends
in a Parameterized
class like on Declare parameter dependencies — Panel v1.3.5 is perfectly valid. Also, I personally prefer registering top-level callbacks that have side-effects with @pn.depends(..., watch=True)
over pn.bind(..., watch=True)
, since when I write these callbacks they usually involve updating some Panel objects so I don’t have a reason to separate them from my user interface code.
winput = pn.widgets.TextInput()
title = pn.pane.Markdown()
@pn.depends(winput, watch=True)
def update_title(input):
title.object = f'# {input}'
pn.Column(winput, title)
.param.watch
is the lowest level API and we don’t recommend it, unless you have special needs that have partially been listed by @johann , to which I’ll add (not exhaustive!):
- you need the callback to be triggered if one of the watched Parameter is set to the same value (
onlychanged=False
)
- you need more fine grained control over what Parameters were updated (when your watch multiple Parameters with
.param.watch
the signature of your callback should be def cb(self, *events)
)
- you need to compare the old and the new value of a Parameter (
event.old
and event.new
)
Updating the example of @johann to cover the case when multiple Parameters are updated together:
import param
class Test(param.Parameterized):
n1 = param.Number()
n2 = param.Number()
def __init__(self, **params):
super().__init__(**params)
self.param.watch(fn=self.view_watch, parameter_names=['n1', 'n2'])
self.param.watch_values(fn=self.view_watch_values, parameter_names=['n1', 'n2'])
def view_watch(self, *events):
print(f'view_watch({self}, events={events})')
def view_watch_values(self, **params):
print(f'view_watch_values({self}, params={params})')
test = Test()
test.param.update(n1=5, n2=2)
# view_watch(<Test Test00004>, events=(Event(what='value', name='n1', obj=Test(n1=5, n2=2, name='Test00004'), cls=Test(n1=5, n2=2, name='Test00004'), old=0.0, new=5, type='changed'), Event(what='value', name='n2', obj=Test(n1=5, n2=2, name='Test00004'), cls=Test(n1=5, n2=2, name='Test00004'), old=0.0, new=2, type='changed')))
# view_watch_values(<Test Test00004>, params={'n1': 5, 'n2': 2})