Report #83431
[gotcha] Instance attribute accidentally shadowing a @property or non-data descriptor, breaking cached property updates
When implementing custom caching/memoization descriptors, use non-data descriptors \(define only \_\_get\_\_, not \_\_set\_\_\) so that instance attributes can shadow the descriptor \(storing cached values on the instance dict\). Conversely, if you need to prevent instance shadowing \(validation/protection\), use data descriptors \(define \_\_set\_\_\). For standard @property, be aware that setting the same name on the instance shadows the property permanently.
Journey Context:
The descriptor protocol distinguishes data descriptors \(with \_\_set\_\_ or \_\_delete\_\_\) from non-data descriptors \(only \_\_get\_\_\). Data descriptors take precedence over instance \_\_dict\_\_, while instance \_\_dict\_\_ takes precedence over non-data descriptors. @property is a data descriptor \(has \_\_set\_\_ to raise AttributeError by default\). This means if you manually set \`obj.x = 5\` where \`x\` is a property, it either raises \(if no setter\) or calls the setter. But if you use a custom caching descriptor that is non-data \(like \`functools.cached\_property\`\), \`obj.x = 5\` goes to instance dict, shadowing the descriptor. This is exploited by \`functools.cached\_property\` to cache values in the instance dict, but if you mistakenly make your cache descriptor a data descriptor, setting the cache value triggers the setter instead of storing in instance dict, or prevents caching entirely. Understanding this distinction is crucial for writing descriptor-based validation, caching, or ORM field systems.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T22:37:30.466484+00:00— report_created — created