Master declarative parameter systems with Param for type-safe configuration. Use this skill when building parameterized classes with automatic validation, creating reactive dependencies with @param.depends, implementing watchers for side effects, auto-generating UIs from parameters, or organizing application configuration with hierarchical parameter structures.
Creates type-safe parameterized classes with automatic validation and reactive UI generation.
npx claudepluginhub uw-ssec/rse-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Master declarative parameter systems with Param and dynamic UI generation. This skill covers building flexible, type-safe, and auto-validated application logic.
Param provides a framework for parameterized objects with automatic validation:
import param
import numpy as np
class DataProcessor(param.Parameterized):
# Basic parameters
name = param.String(default='Processor', doc='Name of processor')
count = param.Integer(default=10, bounds=(1, 1000), doc='Number of items')
scale = param.Number(default=1.0, bounds=(0.1, 10.0), doc='Scale factor')
# String choices
method = param.Selector(default='mean', objects=['mean', 'median', 'sum'])
# Boolean flag
normalize = param.Boolean(default=False)
# List or array
tags = param.List(default=[], item_type=str)
data_array = param.Array(default=np.array([]))
# Instantiate and use
processor = DataProcessor()
print(f"Name: {processor.name}, Count: {processor.count}")
# Validate parameters automatically
processor.count = 500 # OK
processor.count = 2000 # Raises error: out of bounds
class AdvancedConfig(param.Parameterized):
# Date/time parameters
date = param.Date(default='2024-01-01', doc='Start date')
time = param.Time(default='12:00', doc='Start time')
datetime = param.DateTime(default='2024-01-01 12:00:00')
# File/path parameters
input_file = param.Path(default=None, doc='Input file path')
output_dir = param.Fspath(default='.', doc='Output directory')
# Range parameter
value_range = param.Range(default=(0, 10), bounds=(0, 100))
# Color parameter
color = param.Color(default='#FF0000')
# JSON/Dict parameter
config = param.Dict(default={}, per_instance=True)
# DataFrame parameter
dataframe = param.Parameter(default=None)
class DataAnalyzer(param.Parameterized):
data_source = param.Selector(
default='random',
objects=['random', 'sine', 'exponential']
)
amplitude = param.Number(default=1.0, bounds=(0.1, 10.0))
frequency = param.Number(default=1.0, bounds=(0.1, 10.0))
size = param.Integer(default=100, bounds=(10, 10000))
@param.depends('data_source', 'amplitude', 'frequency', 'size')
def get_data(self):
"""Automatically called when any dependency changes"""
np.random.seed(42)
x = np.linspace(0, 2*np.pi, self.size)
if self.data_source == 'random':
return x, np.random.randn(self.size) * self.amplitude
elif self.data_source == 'sine':
return x, self.amplitude * np.sin(self.frequency * x)
else: # exponential
return x, self.amplitude * np.exp(self.frequency * x / 10)
@param.depends('data_source', 'amplitude', 'frequency', 'size')
def summary(self):
"""Display summary that updates automatically"""
x, y = self.get_data()
return f"Mean: {y.mean():.2f}, Std: {y.std():.2f}"
# Use in application
analyzer = DataAnalyzer()
print(analyzer.summary)
# Change parameters
analyzer.amplitude = 2.0
analyzer.frequency = 2.0
print(analyzer.summary) # Updated automatically
Watchers allow you to trigger code when parameters change:
class DataModel(param.Parameterized):
filename = param.String(default='data.csv')
data = param.Parameter(default=None, precedence=-1)
@param.depends('filename', watch=True)
def _load_data(self):
"""Automatically load data when filename changes"""
print(f"Loading {self.filename}...")
# Load file here
self.data = pd.read_csv(self.filename)
# Alternative: explicit watch
def __init__(self, **params):
super().__init__(**params)
self.param.watch(self._on_count_change, 'count')
def _on_count_change(self, event):
print(f"Count changed from {event.old} to {event.new}")
model = DataModel()
model.filename = 'new_file.csv' # Triggers _load_data automatically
class ValidatedModel(param.Parameterized):
email = param.String(default='', doc='Email address')
age = param.Integer(default=0, bounds=(0, 150))
password = param.String(default='')
@param.validators('email')
def validate_email(self, value):
if '@' not in value:
raise ValueError('Invalid email address')
return value
@param.validators('password')
def validate_password(self, value):
if len(value) < 8:
raise ValueError('Password must be at least 8 characters')
return value
def validate_constraint(self):
"""Cross-parameter validation"""
if self.age < 18 and self.email == 'restricted@example.com':
raise ValueError('Minors cannot use this email')
model = ValidatedModel()
model.email = 'invalid' # Raises ValueError
model.password = 'short' # Raises ValueError
class DatabaseConfig(param.Parameterized):
host = param.String(default='localhost')
port = param.Integer(default=5432, bounds=(1, 65535))
username = param.String(default='user')
password = param.String(default='')
class AppConfig(param.Parameterized):
app_name = param.String(default='MyApp')
debug = param.Boolean(default=False)
database = param.Parameter(default=DatabaseConfig())
@param.depends('database.host', watch=True)
def _on_db_change(self):
print(f"Database configuration changed to {self.database.host}")
config = AppConfig()
config.database.host = 'production.db' # Triggers watch on parent
import panel as pn
class DashboardConfig(param.Parameterized):
title = param.String(default='Dashboard')
refresh_interval = param.Integer(default=5000, bounds=(1000, 60000))
metric = param.Selector(default='revenue', objects=['revenue', 'users', 'engagement'])
show_legend = param.Boolean(default=True)
config = DashboardConfig()
# Panel automatically creates UI widgets from parameters
widgets = pn.param.ParamMethod.from_param(config.param)
# Or create individual widgets
title_input = pn.param.TextInput.from_param(config.param.title)
metric_select = pn.param.Selector.from_param(config.param.metric)
interval_slider = pn.param.IntSlider.from_param(config.param.refresh_interval)
import holoviews as hv
class InteractiveDashboard(param.Parameterized):
metric = param.Selector(default='sales', objects=['sales', 'users', 'traffic'])
time_range = param.Range(default=(0, 100), bounds=(0, 100))
aggregation = param.Selector(default='daily', objects=['hourly', 'daily', 'weekly'])
def __init__(self, data):
super().__init__()
self.data = data
@param.depends('metric', 'time_range', 'aggregation')
def plot(self):
filtered = self.data[
(self.data['metric'] == self.metric) &
(self.data['value'] >= self.time_range[0]) &
(self.data['value'] <= self.time_range[1])
]
return filtered.hvplot.line(title=f'{self.metric} ({self.aggregation})')
@param.depends('metric')
def summary(self):
subset = self.data[self.data['metric'] == self.metric]
return f"Mean: {subset['value'].mean():.2f}"
dashboard = InteractiveDashboard(data_df)
pn.extension('material')
app = pn.Column(
pn.param.ParamMethod.from_param(dashboard.param),
pn.Column(dashboard.plot, dashboard.summary)
)
# Group related parameters
class VideoConfig(param.Parameterized):
# Video inputs
input_file = param.Path(doc='Input video file')
start_frame = param.Integer(default=0, bounds=(0, None))
end_frame = param.Integer(default=None, bounds=(0, None))
# Processing
scale = param.Number(default=1.0, bounds=(0.1, 2.0))
quality = param.Selector(default='high', objects=['low', 'medium', 'high'])
# Output
output_format = param.Selector(default='mp4', objects=['mp4', 'webm', 'mov'])
output_path = param.Path(default='output/')
# Bad: string for everything
config = param.Parameterized(
count=param.String(default='10'), # Wrong type
flag=param.String(default='true') # Wrong type
)
# Good: specific types with validation
class ProperConfig(param.Parameterized):
count = param.Integer(default=10, bounds=(1, 100))
flag = param.Boolean(default=True)
class FileProcessor(param.Parameterized):
input_path = param.Path(default=None)
output_path = param.Path(default=None)
# Use watch for file I/O and external effects
@param.depends('input_path', watch=True)
def _process_file(self):
if self.input_path and self.input_path.exists():
self._load_and_process()
# Use depends for computation
@param.depends('input_path')
def file_size(self):
if self.input_path:
return self.input_path.stat().st_size
return None
class WellDocumentedModel(param.Parameterized):
threshold = param.Number(
default=0.5,
bounds=(0, 1),
doc='Classification threshold. Values above this are classified as positive.',
label='Classification Threshold',
precedence=1 # Show first
)
@param.depends('threshold')
def summary(self):
"""Return human-readable summary"""
return f"Using threshold: {self.threshold}"
# Users can access help
help(WellDocumentedModel)
model = WellDocumentedModel()
print(model.param) # Display all parameters with documentation
class AppConfiguration(param.Parameterized):
"""Central configuration object for entire application"""
debug = param.Boolean(default=False)
log_level = param.Selector(default='INFO', objects=['DEBUG', 'INFO', 'WARNING', 'ERROR'])
theme = param.Selector(default='light', objects=['light', 'dark'])
@classmethod
def from_file(cls, path):
"""Load configuration from file"""
import json
with open(path) as f:
config = json.load(f)
return cls(**config)
class Wizard(param.Parameterized):
step = param.Integer(default=0, bounds=(0, 3))
# Step 1: Data input
data_source = param.Selector(default='file', objects=['file', 'database', 'api'])
# Step 2: Processing
algorithm = param.Selector(default='mean', objects=['mean', 'median'])
# Step 3: Output
format = param.Selector(default='csv', objects=['csv', 'json', 'parquet'])
@param.depends('step')
def current_step_view(self):
if self.step == 0:
return f"Select data source: {self.data_source}"
elif self.step == 1:
return f"Choose algorithm: {self.algorithm}"
else:
return f"Output format: {self.format}"
class Statistics(param.Parameterized):
values = param.Array(default=np.array([]))
@property
def mean(self):
return np.mean(self.values)
@property
def std(self):
return np.std(self.values)
@param.depends('values')
def summary(self):
return f"Mean: {self.mean:.2f}, Std: {self.std:.2f}"
watch=True on the @param.depends decorator@param.depends(..., cache_on=[])Activates when the user asks about AI prompts, needs prompt templates, wants to search for prompts, or mentions prompts.chat. Use for discovering, retrieving, and improving prompts.