Skip to content

gh-102618: Introduce a functools.cached_method decorator#150002

Open
sirosen wants to merge 1 commit into
python:mainfrom
sirosen:cached-method
Open

gh-102618: Introduce a functools.cached_method decorator#150002
sirosen wants to merge 1 commit into
python:mainfrom
sirosen:cached-method

Conversation

@sirosen
Copy link
Copy Markdown
Contributor

@sirosen sirosen commented May 18, 2026

resolves #102618

This definition of cached_method is based on the discussion on DPO:
https://discuss.python.org/t/107164

Some tradeoffs need to be made in any version of such a decorator. In
this particular implementation, the choices made are as follows:

  • lru_cache will be used under the hood, and cache_clear() and
    cache_info() will be exposed

  • caches will be stored in a separate dict, indexed by id(self) --
    meaning instances do not need to be hashable and will not share caches

  • weakrefs will be used to delete entries from the cache, so the
    instances must be weak-referencable

  • lru_cache itself is not threadsafe, but initialization of the caches
    is threadsafe -- this avoids confusing scenarios in which cache entries
    "disappear"

New documentation is included, marked for 3.16, and a small number of new
tests are added.


In addition to @rhettinger's review, as the listed owner for functools, @sobolevn expressed interest in reviewing changes.
@kenahoo and @stevendaprano also want a look based on their prior interest.

Everyone I've spoken with about this change1 has been supportive of the core idea. In particular @jaraco noted that he would support this addition, and maintains a very different implementation. There are many other ways of writing this, with different tradeoffs. Perhaps the most notable class of those are versions which store caches on the instance itself, like this one suggested here in the DPO discussion -- this writes the cached method into __dict__, similarly to cached_property.

Of course, I'm happy to alter or re-approach any part of this per review! 😄

Footnotes

  1. at PyCon US 2026

@read-the-docs-community
Copy link
Copy Markdown

read-the-docs-community Bot commented May 18, 2026

Documentation build overview

📚 cpython-previews | 🛠️ Build #32741614 | 📁 Comparing a5b4b0d against main (1692854)

  🔍 Preview build  

2 files changed
± library/functools.html
± whatsnew/changelog.html

resolves python#102618

This definition of `cached_method` is based on the discussion on DPO:
   https://discuss.python.org/t/107164

Some tradeoffs need to be made in any version of such a decorator. In
this particular implementation, the choices made are as follows:

- lru_cache will be used under the hood, and `cache_clear()` and
  `cache_info()` will be exposed

- caches will be stored in a separate dict, indexed by `id(self)` --
  meaning instances do not need to be hashable and will not share caches

- weakrefs will be used to delete entries from the cache, so the
  instances must be weak-referencable

- lru_cache itself is not threadsafe, but initialization of the caches
  is threadsafe -- this avoids confusing scenarios in which cache entries
  "disappear"

New documentation is included, marked for 3.16, and a small number of new
tests are added.
Comment thread Lib/functools.py
return self

def __get__(self, instance, owner=None):
# similar to singledispatch(), we want to defer use of weakref until/unless it
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: imperative voice is cleaner

Suggested change
# similar to singledispatch(), we want to defer use of weakref until/unless it
# similar to singledispatch(), defer use of weakref until/unless it

Comment thread Lib/functools.py
update_wrapper(self, func)
return self

def __get__(self, instance, owner=None):
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for proposing this, @sirosen, and especially for the call out on the jaraco.functools implementation. One of the caveats of that implementation is that you can't further wrap the resulting object (e.g. @property\n@cached_method). Does this approach satisfy that use case or does it have the same limitation? If it has the limitation, it should be called out in the docs (or at least the doc string), and maybe should have a test capturing the known limitation. If it doesn't have the limitation, does that mean that cached_property could potentially be superseded by a composition of property and cached_method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This still has the problem, although maybe it can be solved (?). I'll poke at the possibilities but will probably make a doc update shortly.

Right now, if you try to stack this with @property, it will emit a TypeError when called because the same cached_method instance gets assigned multiple times.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add a functools.cache variant for methods to avoid keeping instances alive

2 participants