Design pattern for reactive calculation dependent on multiple filtered data sets

Question
I’ve recently started using Panel again to develop a small app to do a calculation and display the results. I’m using the class design pattern the app and using the @pn.depends decorator to define the reactivity.

The app allows the user to select various options, which filters the original data sets, and then performs a calculation when the filtered datasets are updated. A simplified example with dummy data is given below.

However, when I start the app, I get a TypeError: 'NoneType' object is not subscriptable. From debugging, I think is is because on initialisation dataset1_filtered and dataset2_filtered are empty, but the calculation is still trying to run as the parameters are initialised.

Is there a better design pattern for doing something like this? Effectively, I don’t want the calculation to run until all other parameters have been initialised and the data sets have been filtered. Would using reactive expressions (i.e. rx()) be more suitable?

Open to thoughts and ideas.

Simplified example app

"""Example app"""

import random

import pandas as pd
import panel as pn
import param
from panel.viewable import Viewer


@pn.cache
def get_dataset1():
    """Get dataset 1. This is dummy data."""
    df = pd.read_csv("land_use_change_carbon_factors.csv")
    df = pd.DataFrame(
        {
            "A": [1, 2, 3, 4, 5],
            "B": ["foo", "bar", "foo", "bar", "foo"],
            "C": [random.randint(1, 10) for i in range(5)],
        }
    )
    return df


@pn.cache
def get_dataset2():
    """Get the dataset 2. This is dummy data."""
    df = pd.DataFrame(
        {
            "A": [1, 2, 3, 4, 5],
            "BB": ["A", "A", "B", "B", "C"],
            "CC": [random.randint(10, 20) for i in range(5)],
            "DD": [random.randint(100, 200) for i in range(5)],
        }
    )
    return df


class CarbonChangeCalculator(Viewer):
    """The carbon change calculator app."""

    data1 = param.DataFrame()
    data2 = param.DataFrame()

    A = param.Selector(objects=[1, 2, 3, 4, 5])
    B = param.Selector(objects=["foo", "bar"])
    BB = param.Selector(objects=["A", "B", "C"])
    factor = param.Number(default=10)

    data1_filtered = param.DataFrame()
    data2_filtered = param.DataFrame()

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

    @pn.depends("A", "B", watch=True, on_init=True)
    def _update_data1_filtered(self):
        self.data1_filtered = self.data1[
            (self.data1["A"] == self.A) & (self.data1["B"] == self.B)
        ]

    @pn.depends("A", "BB", watch=True, on_init=True)
    def _update_data2_filtered(self):
        self.data2_filtered = self.data2[
            (self.data2["A"] == self.A) & (self.data2["BB"] == self.BB)
        ]

    @pn.depends("data1_filtered", "data2_filtered", "factor", watch=True)
    def calculate(self):
        a = self.data1_filtered["C"].sum() * self.factor
        b = self.data2_filtered["CC"].sum()
        return a + b

    def __panel__(self):
        return pn.Row(
            pn.Column(
                self.param.A,
                self.param.B,
                self.param.BB,
                self.param.factor
            ),
            pn.Column(
                pn.pane.DataFrame(self.param.data1_filtered),
                pn.pane.DataFrame(self.param.data2_filtered)
            ),
            pn.Column(
                pn.pane.Markdown("## Results"),
                pn.pane.Markdown("The calculated value is:"),
                pn.rx(self.calculate)
            )
        )


data1 = get_dataset1()
data2 = get_dataset2()
app = CarbonChangeCalculator(data1=data1, data2=data2)
app.servable()

Took a quick skim. Replacing on_init=True with self.param.trigger("A") inside __init__ after super().__init__ should fix it if I understand correctly.