Caching that resets itself under certain circumstances: Reduce boiler plate code
This might be a job for @functools.cached_property. It works a lot like @property, except it remembers the value between calls. The value can then be cleared using del in the fit function, or elsewhere if needed.
Unfortunately, the properties can't be deled until they've been set, which means we have to re-add the boiledplate in fit in the form of try: del x; except AttributeError: pass. Maybe that's an improvement since it's at least centralized, but it's still not ideal.
There are workarounds like
def fit(self, data):
    self.intermediate_data_ = ...

    for prop in [&quot;complex_transform1&quot;, &quot;complex_transform2&quot;]:
        try:
            delattr(self, prop)
        except AttributeError:
            pass

    return self

Or perhaps something like
def fit(self, data):
    self.intermediate_data_ = ...

    for prop in self.reset_on_fit:
        delattr(self, prop)

    self.reset_on_fit = []
    return self

with the properties looking like
@cached_property
def complex_transform1(self):
    self.reset_on_fit.append(&quot;complex_transform1&quot;)
    return self.intermediate_data_ / self.parameter / 2

But putting that data into a hardcoded string is not very refactor-friendly. Perhaps we can define a decorator to figure that out for us?
Well, I haven't tested it very thoroughly, but I think we can:
from functools import cached_property, wraps

def resets_on_fit(fn):
    @wraps(fn)
    def wrapper(self):
        self.reset_on_fit.append(fn.__name__)
        return fn(self)

    return wrapper

class MyClass:
    # ...

    def fit(self, data):
        self.intermediate_data_ = data + self.parameter + 2
        
        for prop in self.reset_on_fit:
            delattr(self, prop)
        
        self.reset_on_fit = []
        return self

    @cached_property
    @resets_on_fit
    def complex_transform1(self):
        return self.intermediate_data_ / self.parameter / 2