Param metadata: readonly and constant

Hi - I have been using the following method for setting up params that can be updated from the code but not by a user:

my_param = param.Parameter(constant=True)

and then, e.g.:

    with param.edit_constant(self):
        self.my_param += 1

I found this in an old post.

Questions:

  • Is this still the recommended way to handle this?
  • What is the difference between constant and readonly? The documentation is not really explicit about this, so far as I can see.

Many thanks.

This is the original post, by @Marc :

OK, think I have the answer to the second question reading through the documentation again:

  • constant means the parameter is instantiated with the default or the value supplied at instantiation, and can’t be updated thereafter
  • readonly is instantiated with the default value and cannot be set to another value, even at instantiation

Is that right?

It would be useful to have an explicit compare and contrast in the docs because by their names it’s hard to understand what the intended difference between them would be.

1 Like

Yes indeed the naming of constant and readonly is unfortunate:

  • if you focus solely on readonly, it really means what it does, once set the value of that Parameter cannot be changed, be it at the class or instance level or with some updating API.
  • read constant as constant_after_init, once an instance is created the Parameter value can only be updated with param.edit_constant

Here’s some code that will hopefully help you better understand the differences between readonly and constant:

Summary
from contextlib import contextmanager

@contextmanager
def simple_error():
    try:
        yield
    except Exception as e:
        print(type(e).__name__, ':', e)

import param

class P(param.Parameterized):
    rp = param.Parameter(1, readonly=True)
    c = param.Parameter(1, constant=True)
    n = param.Parameter(1)

### `readonly`

with simple_error():
    P.rp = 2

with simple_error():
    P(rp=2)

p = P()
with simple_error():
    p.rp = 2

### `constant`

# Ok before instantiation
P.c = 2

# Ok during instantiation
P(c=3)

p = P()
with simple_error():
    p.c = 4

# This is not so well known to Param users, Parameters act as class variables
# if not set in the constructor or after instantiation.
p1 = P()
p2 = P()
# Modifying the class value will only affect the Parameters that have constant=False (default)
P.n = P.c = 10
print(p1.n, p1.c)
print(p2.n, p2.c)
1 Like

@maximlt — thanks, yes that example is very clear.

I suppose really the names should have been the other way around if anything, because ‘constant’ is the one that is ‘readonly’ for the user, and the one that is not actually constant because it can be edited with edit_constant. But anyway, things have their history and turn out as they do for what must have been good reasons at some point.

1 Like

PS If you think it’s a good idea, I could raise an issue on GitHub to suggest something like your example be included in the docs.