DHI / DHI/python-package-development
Add fluent interface pattern as teaching example
- Dominant language
- Jupyter Notebook
- Stars
- 8
- Forks
- 1
- Avg merge
- 4m
- Merged PRs (30d)
- 1
Description
Add a section or exercise covering API design patterns for simplifying functions with many parameters.
Good reference example: https://github.com/DHI/modelskill/discussions/492 — the `scatter()` function has grown a long signature:
```python
def scatter(
x: np.ndarray,
y: np.ndarray,
*,
bins: int | float = 120,
quantiles: int | Sequence[float] | None = None,
fit_to_quantiles: bool = False,
show_points: bool | int | float | None = None,
show_hist: Optional[bool] = None,
show_density: Optional[bool] = None,
norm: Optional[colors.Normalize] = None,
backend: Literal["matplotlib", "plotly"] = "matplotlib",
figsize: Tuple[float, float] = (8, 8),
xlim: Optional[Tuple[float, float]] = None,
ylim: Optional[Tuple[float, float]] = None,
reg_method: str | bool = "ols",
title: str = "",
xlabel: str = "",
ylabel: str = "",
skill_table: Optional[str | Sequence[str] | Mapping[str, str] | bool] = False,
skill_scores: Mapping[str, float] | None = None,
skill_score_unit: Optional[str] = "",
ax: Optional[Axes] = None,
**kwargs,
) -> Axes:
```
### Alternative patterns to simplify
**1. Fluent interface (method chaining)**
Each component gets its own method. Easy to add/remove parts.
```python
(Comparer(x, y)
.plot()
.scatter(alpha=0.5)
.qq([0.05, 0.5, 0.75, 0.95])
# .reg_line(equation=True)
.skill_table(("n", "bias"))
).show()
```
**2. Configuration objects (dataclasses)**
Group related parameters into typed config objects.
```python
@dataclass
class ScatterStyle:
bins: int = 120
show_points: bool = True
show_density: bool = False
norm: colors.Normalize | None = None
@dataclass
class Layout:
figsize: tuple[float, float] = (8, 8)
xlim: tuple[float, float] | None = None
ylim: tuple[float, float] | None = None
title: str = ""
xlabel: str = ""
ylabel: str = ""
scatter(x, y, style=ScatterStyle(bins=50), layout=Layout(title="My plot"))
```
**3. Presets / named styles**
Offer common configurations as named presets, with overrides.
```python
scatter(x, y, preset="minimal") # just points + 1:1 line
scatter(x, y, preset="full") # density + qq + regression + skill table
scatter(x, y, preset="presentation") # large fonts, clean layout
scatter(x, y, preset="minimal", title="Hm0") # preset + override
```
**4. Composition of small functions**
Instead of one function that does everything, provide building blocks that work with a standard Axes.
```python
fig, ax = plt.subplots()
plot_scatter(ax, x, y, show_density=True)
plot_qq(ax, x, y, quantiles=[0.25, 0.5, 0.75])
plot_reg_line(ax, x, y)
add_skill_table(ax, x, y, metrics=["bias", "rmse"])
```
Each pattern has trade-offs worth discussing: discoverability, type safety, composability, backwards compatibility, learning curve.
Relevant topics: method chaining, `Self` return type, builder pattern, dataclasses as config, API design trade-offs.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.