Reinitialize a Parameter

How does one reinitialize a parameter back to its initial settings. For example, suppose I have a parameterized class that represents a user input form and I want to be able to reset it for multiple uses.

import param
import panel as pn
pn.extension()


class Form(param.Parameterized):
    """ Imagine a more complex form that tracks user inputs """
    my_input = param.String('abc', doc='Help')    
    
    
class App(param.Parameterized):
    """ An application that uses the form"""
    form = param.Parameter(Form())
    
    @param.depends('form.my_input')
    def render(self):
        return pn.Column(
            pn.Param(self.form.param), 
            pn.pane.Markdown(self.form.my_input), 
            app.param.reinitialize)

    def _reinitialize(self):
        """ Click me if you want to restart with a new form """
        self.form = Form() # This doesn't actually work
        self.param.trigger('form')
    reinitialize = param.Action(_reinitialize)
    
# Create a UI
pn.Row(App().render).servable()

The reinitialization severs all interactivity.

Likely a DEFAULTS dictionary containing the original values. Then manually resetting the values upon submission.

Hi @jbogaardt

You can just loop over the parameters to reset to the default like in the _reset function below.

import param
import panel as pn

pn.extension(sizing_mode="stretch_width")

class Form(param.Parameterized):
    """ Imagine a more complex form that tracks user inputs """
    my_input = param.String('abc', doc='Input 1')
    my_input2 = param.String('efg', doc='Input 2')

    def __init__(self, **params):
        super().__init__(**params)

    def _reset(self):
        for param in self.param:
            if param not in ["name"]:
                setattr(self, param, self.param[param].default)

    action = param.Action(_reset, label="Reset Form")

class App(pn.viewable.Viewer):
    """ An application that uses the form"""
    form = param.Parameter()


    def __init__(self, **params):
        params["form"]=params.get("form", Form())

        super().__init__(**params)

        self._layout = pn.Column("## Input", self.form, "## Output", self.my_output)

    @pn.depends("form.my_input", "form.my_input2")
    def my_output(self):
        return self.form.my_input + ' ' + self.form.my_input2

    def __panel__(self):
        return self._layout

# Create a UI
App().servable()
2 Likes

Thanks guys. This is a very straight-forward solution, but I missed it because my mental model for how param works is not complete.

It is such a common pattern to update a parameter by assignment to new data of the same type. Assigning to defaults like you did does just that. But why doesn’t that pattern extend to assigning new instances of a parameterized class?

Not being able to articulate the why myself makes me feel like there is a core concept of param that I am missing.

You can also make it work by replacing with a new form. But I felt it became a bit too complicated because the @pn.depends binds to the specific first form. Not any later form.

You will have to use the .watch api instead and first remove the watcher for the old form and then create a new watcher for the new form.

At least that is my understanding ???

image

But I would also like to understand if there is an easy pattern for resetting by creating a new form.

Any input @philippjfr ?

2 Likes

This is the fundamental problem here, Param should unbind the original Form and rebind the new form on assignment. There’s an issue for this here and I may just tackle this today. I agree that for now you’ll have to fall back to the watch API here and do this manually.

2 Likes

Started working on this here: https://github.com/holoviz/param/pull/533

Unfortunately a very hairy bit of code and the foundation of pretty much everything. So will need a lot of testing before we can release it.

2 Likes