There is no default_factory for Param objects?

If I want to define a dataclass field that chooses a value via a zero-argument lambda, I can do the following:

marketer_id = itertools.count(1)

@dataclass
class Marketer:
    id: int = field(default_factory=lambda: next(marketer_id))

But it seems I must do the following in Param, correct?

class Marketer(Parameterized):
    id = Number()

    def __init__(self, **params):
        super().__init__(**params)
        self.id = next(marketer_id)
2 Likes

This would indeed be great to have. As far as I’m aware you’re correct but I think this is a good feature request.

Param does support a default_factory, but (a) it calls it compute_default_fn, and (b) it’s only supported for Selector types. I assume that whoever implemented it for Selector was solving a specific problem rather than realizing it would apply to all Parameter types just as well.

Most non-Selector types inherit from Dynamic, so I tried to brainstorm whether there was a way to use Dynamic along with instantiated Parameters to get the same effect, but I’m pretty sure there isn’t a way to do that. First, Dynamic parameters don’t accept iterators or generators, only true callables, and even if you make the factory into a callable, I don’t think there’s currently a way to force the value to be computed at instantiation time. Instead, it stays dynamic:

import itertools
from param import Parameterized, Number
marketer_id = itertools.count(1)

class Marketer(Parameterized):
    id = Number(lambda: next(marketer_id), instantiate=True, constant=True)

m1 = Marketer()
m2 = Marketer()
m1, m2

(Here the intent was to make m1.id have a single constant value, but instead it keeps updating.)

So I agree that we’d need additional functionality to get this. The most obvious way to do that is to push the compute_default_fn logic down from Selector into Parameter, which seems easy enough to do.

Another alternative would be to push the functionality of Dynamic up to Parameter, while adding an option to evaluate the Dynamic value only a single time, at instantiation. I think that option is cleaner and more general than a default_factory approach, but it’s also a lot more work and would be more disruptive, and I don’t think many people are currently using Dynamic and thus would benefit from this generality.

So on balance, my vote would be to push the compute_default_fn logic down from Selector into Parameter. Anyone up for implementing that and seeing if it breaks tests or applications? I’d hope it would be a safe change to make!

1 Like

If we’re already moving it up can we call the generalized version something more standard, like default_factory?

3 Likes

Just for the record. There is a request for this feature and a discussion in factory function for Parameter instead of default value + instantiate=True · Issue #508 · holoviz/param (github.com)