Clarification of Param based methods behaviour when no dependancy is declared

Hello! In working on a dashboard that is built using Param I noticed that some plots that I didn’t expect to be ‘updating’ were being run multiple times whenever I selected a new value on one of the params on the dashboard.

A minimal example of what I mean based on the Google map example:

import param
import panel as pn

pn.widgets.Tabulator.theme = 'bootstrap4'

class GoogleMapViewer(param.Parameterized):

    continent = param.ObjectSelector(default='Asia', objects=['Africa', 'Asia', 'Europe'])

    country = param.ObjectSelector(default='China', objects=['China', 'Thailand', 'Japan'])

    _countries = {'Africa': ['Ghana', 'Togo', 'South Africa', 'Tanzania'],
                  'Asia'  : ['China', 'Thailand', 'Japan'],
                  'Europe': ['Austria', 'Bulgaria', 'Greece', 'Portugal', 'Switzerland']}

    @param.depends('continent', watch=True)
    def _update_countries(self):
        countries = self._countries[self.continent]
        self.param['country'].objects = countries
        self.country = countries[0]

    @param.depends('country')
    def view(self):
        iframe = """
        <iframe width="800" height="400" src="https://maps.google.com/maps?q={country}&z=6&output=embed"
        frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>
        """.format(country=self.country)
        return pn.pane.HTML(iframe, height=400)

    def do_not_update(self):
        print("I have run")
        return pn.pane.Markdown("Do not update.")


material = pn.template.MaterialTemplate(title='Test')

viewer = GoogleMapViewer(name='Google Map Viewer')

material.sidebar.append(viewer.param)
material.main.append(viewer.view)
material.main.append(viewer.do_not_update)

material.servable()

When running the above, every time I change either the continent or country I see that the do_not_update is running each time in the output (and twice each time for the continent and then country change):

Europe
Austria
I have run
Europe
Austria
I have run
Africa
Ghana
I have run
Africa
Ghana
I have run
Africa
South Africa
I have run

Now I’m guessing that there is something that if no dependancies are declared then the view is updated on any param change, as if I do the following and add a dummy param:

import param
import panel as pn

pn.widgets.Tabulator.theme = 'bootstrap4'

class GoogleMapViewer(param.Parameterized):

    continent = param.ObjectSelector(default='Asia', objects=['Africa', 'Asia', 'Europe'])

    country = param.ObjectSelector(default='China', objects=['China', 'Thailand', 'Japan'])

    dummy = param.ObjectSelector(precedence=-1)

    _countries = {'Africa': ['Ghana', 'Togo', 'South Africa', 'Tanzania'],
                  'Asia'  : ['China', 'Thailand', 'Japan'],
                  'Europe': ['Austria', 'Bulgaria', 'Greece', 'Portugal', 'Switzerland']}

    @param.depends('continent', watch=True)
    def _update_countries(self):
        countries = self._countries[self.continent]
        self.param['country'].objects = countries
        self.country = countries[0]

    @param.depends('country')
    def view(self):
        iframe = """
        <iframe width="800" height="400" src="https://maps.google.com/maps?q={country}&z=6&output=embed"
        frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>
        """.format(country=self.country)
        return pn.pane.HTML(iframe, height=400)

    @param.depends('dummy')
    def do_not_update(self):
        print(self.continent)
        print(self.country)
        print("I have run")
        return pn.pane.Markdown("Do not update.")


material = pn.template.MaterialTemplate(title='Test')

viewer = GoogleMapViewer(name='Google Map Viewer')

material.sidebar.append(viewer.param)
material.main.append(viewer.view)
material.main.append(viewer.do_not_update)

material.servable()

then I see that it no longer runs.

Am I correct in this thinking? So I have to make sure I declare dependencies carefully for all param methods? Apologies if I’ve missed this in the docs, I’ve read the param panel page a few times and couldn’t see this kind of behaviour stated anywhere. Thanks in advance!

My understanding is that if you declare a method on a Parameterized class but don’t specify any specific dependencies, then it depends on all parameters.

To avoid it updating you could then just use

material.main.append(viewer.do_not_update())

instead of

material.main.append(viewer.do_not_update)

My understanding is that if you declare a method on a Parameterized class but don’t specify any specific dependencies, then it depends on all parameters.

Yes indeed, this is written in the docs (it’s somewhat buried in this large page):
https://param.holoviz.org/user_guide/Dependencies_and_Watchers.html#watch-false-dependencies

That says:

because Param conservatively assumes that any method could read the value of any parameter, and thus if it has no other declaration from the user, the dependencies are assumed to include all parameters (including name, even though it is constant)

and

Conversely, if you want to declare that a given method does not depend on any parameters at all, you can use @param.depends().

2 Likes

Thanks very much @maximlt.

Gah, I totally missed digging through the actual param docs, I will have a read!

2 Likes