#30278 closed Bug (invalid)
Using del on uncalled cached_property throws exception.
Reported by: | Matthew Schinckel | Owned by: | nobody |
---|---|---|---|
Component: | Utilities | Version: | dev |
Severity: | Normal | Keywords: | cached_property |
Cc: | Triage Stage: | Unreviewed | |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
It's explicitly documented that you may use del
(or delattr
) to force a cached_property to recalculate the next time it's called.
However, if you try to del
a cached_property that has never been evaluated, then an AttributeError is raised.
There doesn't seem to be a way to detect if a cached_property has been evaluated or not, as hasattr(foo, 'bar')
results in the evaluation of the cached_property.
It's entirely possible that this is something that is beyond the capability of the way cached_property is implemented, but it still feels wrong to me.
(Likewise, if you attempt to del foo.bar
twice without evaluation in between, you will raise an AttributeError).
Change History (9)
comment:1 by , 6 years ago
comment:2 by , 6 years ago
Gotcha.
(In practice, it's possible to catch an exception raised by code that is calling del foo.bar
when it's possible foo.bar
has not been referenced yet, so I guess that will have to do).
comment:3 by , 6 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
follow-up: 5 comment:4 by , 6 years ago
Great discussion. Thank you both!
Just before this disappears over the horizon, is it worth adjusting the del
example in the docs here do we think?
comment:5 by , 6 years ago
Replying to Carlton Gibson:
Just before this disappears over the horizon, is it worth adjusting the
del
example in the docs here do we think?
I did wonder that: maybe it is worth having a note in the docs.
The problem here is (a) fundamental to how Python works, and (b) complicated by
cached_property
"cheating".So,
cached_property
works by saving the value it calculates into the instances__dict__
.This means next time that attribute is accessed, _Python_ will check
__dict__
first, and not even try the property.Calling
del
will remove the entry from the instances__dict__
. However, if it's not there, it will fall back to the class's property.This causes the attribute error, because the property class does not have a
__delete__
method.If it did have a
__delete__
method, Python's lookup would differ, and it would _not_ check__dict__
before calling the property. (the official documentation mentions this about__get__
, but a simple test shows it applies to__delete__
equally)https://docs.python.org/3/reference/datamodel.html#invoking-descriptors
So, we can't have _both_ - if we support del on uninvoked
cached_property
we lose the utility ofcached_property
(or, at least, the performance)