diff --git a/README.md b/README.md
index 1725fac..8391076 100644
--- a/README.md
+++ b/README.md
@@ -10,14 +10,44 @@
# `ParamClass`
+A Python library that brings robust attribute protection to parameter-holding classes, making inheritance safer and more predictable.
+
```bash
-# Install from PyPI
pip install paramclasses
```
+## TLDR 🚀
+
+- Like dataclasses, but with protected attributes that can't be accidentally overridden
+- Perfect for building extensible APIs and libraries
+- Runtime protection (no type checker needed)
+- Simple to use - just inherit from `ParamClass` and use `@protected`
+
+```python
+from paramclasses import ParamClass, protected
+
+class BaseEstimator(ParamClass):
+ learning_rate: float = 0.01 # Parameter with default
+ n_iterations: int # Required parameter
+
+ @protected # Can't be overridden by subclasses
+ def fit(self, data):
+ # Your fitting logic here
+ pass
+
+# This will raise ProtectedError - can't override protected method
+class BadEstimator(BaseEstimator):
+ fit = "oops" # ❌ Raises ProtectedError
+
+# This is fine - proper inheritance
+class GoodEstimator(BaseEstimator):
+ def predict(self, X): # ✅ Adding new methods is fine
+ return X * 2
+```
+
###### Table of Contents
-1. [👩🏫 **Rationale**](#1-rationale-)
+1. [🤔 **Why ParamClass?**](#1-why-paramclass-)
2. [🧐 **Overview**](#2-overview-)
- [Defining a _paramclass_](#defining-a-paramclass)
- [Protecting attributes with `@protected`](#protecting-attributes-with-protected)
@@ -41,27 +71,20 @@ pip install paramclasses
6. [⚖️ **License**](#6-license-%EF%B8%8F)
-## 1. Rationale 👩🏫
-
-##### Parameter-holding classes vs. inheritance...
-
-For a _parameter_-holding class, like [dataclasses](https://docs.python.org/3/library/dataclasses.html), it would be nice to embark some inherited functionality -- _e.g._ `params` property to access current `(param, value)` pairs, `missing_params` for unassigned parameter keys,... Such inheritance would allow to factor out specialized functionality for context-dependant methods -- _e.g._ `fit`, `reset`, `plot`, etc... However, such subclassing comes with a risk of attributes conflicts, especially for libraries or exposed APIs, when users do not necessarily know every "read-only" (or "**protected**") attributes from base classes.
-
-##### Our solution 😌
-
-To solve this problem, we propose a base `ParamClass` and an `@protected` decorator, which robustly protects any target attribute -- not only parameters -- from being accidentally overriden when subclassing, at runtime. If a subclass tries to override an attribute protected by one of its parents, a detailed `ProtectedError` will be raised and class definition will fail.
-
-##### Why not use `@dataclass(frozen=True)` or `typing.final`?
-
-First of all, the `@dataclass(frozen=True)` decorator only applies protection to instances. Besides, it targets all attributes indifferently. Morover, it does not protect against deletion or direct `vars(instance)` manipulation. Finally, protection is not inherited, thus subclasses need to use the decorator again, while being cautious not to silently override previously protected attributes.
-
-The `typing` alternatives [`@final`](https://docs.python.org/3/library/typing.html#typing.final) and [`Final`](https://docs.python.org/3/library/typing.html#typing.Final) are designed for type checkers only, which we do not want to rely on. From python 3.11 onwards, `final` _does_ add a `__final__` flag when possible, but it will not affect immutable objects.
+## 1. Why ParamClass? 🤔
-We also mention this [recent PEP draft](https://peps.python.org/pep-0767/) considering attribute-level protection, again for type checkers and without considering subclassing protection.
+Ever tried building a library with inheritance and parameter-holding classes, only to find users accidentally overriding your critical attributes? ParamClass solves this by providing:
-##### Disclaimer
+- Runtime protection for your critical attributes and methods
+- Clean parameter handling like dataclasses
+- Safe inheritance that prevents accidental overrides
+- Clear error messages when protection is violated
-Note that the protection provided by _paramclasses_ is very robust for **practical use**, but it **should not** be considered a security feature.
+Unlike alternatives like `@dataclass(frozen=True)` or `typing.Final`, ParamClass:
+- Protects specific attributes, not everything
+- Works at the class level, not just instances
+- Provides inheritance-aware protection
+- Doesn't rely on type checkers
Back to [Table of Contents](#readme)👆
@@ -340,7 +363,7 @@ TypeError: 'list' object cannot be interpreted as an integer
>
```
-Note how `NonParamOperator().op` is a **bound** method. What happened here is that since `np.cumsum` is a data [descriptor](https://docs.python.org/3/howto/descriptor.html) -- like all `function`, `property` or `member_descriptor` objects for example --, the function `np.cumsum(a, axis=None, dtype=None, out=None)` interpreted `NonParamOperator()` to be the array `a`, and `[0, 1, 2]` to be the `axis`.
+Note how `NonParamOperator().op` is a **bound** method. What happened here is that since `np.cumsum` is a data [descriptor](https://docs.python.org/3/howto/descriptor.html#member-objects-and-slots), the function `np.cumsum(a, axis=None, dtype=None, out=None)` interpreted `NonParamOperator()` to be the array `a`, and `[0, 1, 2]` to be the `axis`.
To avoid this kind of surprises we chose, **for parameters only**, to bypass the get/set/delete descriptor-specific behaviours, and treat them as _usual_ attributes. Contrary to [dataclasses](https://docs.python.org/3/library/dataclasses.html), by also bypassing descriptors for set/delete operations, we allow property-valued parameters, for example.
```python