import functools
class C:
@functools.cached_property
def foo(self):
print('expensive calc')
return 42
def reset(self):
del self.foo
The following works as expected:
c = C()
print(c.foo)
print(c.foo)
c.reset()
print(c.foo)
giving:
$ ./u.py
expensive calc
42
42
expensive calc
42
But, if the del is invoked before the property is created (or, equivalently, twice in a row), then we see an AttributeError:
$ ./u.py
expensive calc
42
42
expensive calc
42
Traceback (most recent call last):
File "/home/nexus/./u.py", line 20, in <module>
c.reset()
File "/home/nexus/./u.py", line 12, in reset
del self.foo
^^^^^^^^
AttributeError: 'C' object has no attribute 'foo'
Is there an official way to detect that the property is currently not present, so deleting it makes no sense, or is wrapping it in a try/except AttributeError the correct approach?
Sorry I don’t know the answer for certain. To be honest I would consider it best practice not to use @cached_property in a situation where you need to reset it. That sounds like it’s asking for bugs. I’m curious why you need this. Could you instead use a pattern like
class C:
@property
def foo(self):
return _f(self.maybe_changed_attributes) # or self._m(self.maybe_changed_attributes)
@lru_cache(1)
def _f(...):
print('expensive calc')
return 42
Yeah, catching an AttributeError seems reasonable. You could use hasattr to check if you really wanted to. Finally, you could also mutate the instance’s __dict__ e.g. via pop("foo", None)
Out of habit, I kept using dir(c) instead of c.__dict__, and foo was always present. In practice, I have not had a need to use __dict__ in years, and maybe then, probably only once in some random library, and forgot that the two are not the same.
It wasn’t until it was mentioned so many times in the discussion did I realized I missing something so fundamental.
If more than removing the entry is necessary, then if ... in is probably more appropriate.
Otherwise, they are likely equivalent and comes down to a style question.
Personally, I would opt for if ... in for the same reason I always use if (...) { stuff } instead of if (...) stuff in brace-style languages. I may not need more than one line today, but I might tomorrow, so let’s stay consistent. E.g., my “style”.