Hi again!
I think this question corresponds to another slightly vague aspect of the documentation. Or rather, an area in which the documentation might be helpfully more opinionated about the best way to do something.
For many parameterized classes, there will be two sets of properties.
- parameters in the strict sense: properties that were supplied as arguments or defaults in the constructor
- calculated properties: properties whose values are generated by the constructor or by other methods, usually on the basis of (1)
(1) is clearly what param was made for. There are 3 ways I can think of for dealing with (2).
- Code them as param.Parameters in the same way as properties of type (1), using
constant=True
to prevent them being set outside the class. - Code them as ordinary variables, using @param.depends, or a constructor, to recalculate or initialise them as needs be.
- Finally, instead of making them variables, they could be written as methods. That seems a common approach in the docs.
So my question to those of you experienced in the ways of param is:
Which of these is the most in tune with param, or which do you prefer and why? Or what are the advantages and disadvantages of each?
Initially in my trying out param, I assumed the first would be the best because it imposed a tidy unity on the code. However, I’ve realised that it causes some problems, for instance, a panel.Param widget will include all the calculated properties.
e.g.:
class Triangle(param.Paramerterized):
# properties defined as Parameters,
# but only the first two are parameters in the strict sense
a = param.Number(2, bounds=(0, None))
b = param.Number(3, bounds=(0, None))
c = param.Number(bounds=(0, None), constant=True)
@param.depends('a', 'b', watch=True)
def _update_c(self):
with param.edit_constant(self):
self.c = a**2 + b**2
The second, which I’ve been trying out, is a bit more logical, but it seems to be mixing fish and foul. For instance, I don’t really feel comfortable with this without some typing, which doesn’t seem to be in the spirit of param:
class Triangle(param.Paramerterized):
# parameters in the strict sense
a = param.Number(2, bounds=(0, None))
b = param.Number(3, bounds=(0, None))
# a calculated property
c: float
@param.depends('a', 'b', watch=True)
def _update_c(self):
self.c = a**2 + b**2
Finally, this seems to be what the docs favour, but if the calculation is demanding I suppose it might cause performance issues…
class Triangle(param.Paramerterized):
# parameters in the strict sense
a = param.Number(2, bounds=(0, None))
b = param.Number(3, bounds=(0, None))
def c(self):
return a**2 + b**2