Skip to content

Commit 981013b

Browse files
committed
Move cached_method above cached_property
And add it to functools' `__all__` list.
1 parent 365c484 commit 981013b

2 files changed

Lines changed: 98 additions & 98 deletions

File tree

Doc/library/functools.rst

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,51 @@ The :mod:`!functools` module defines the following functions:
5757
.. versionadded:: 3.9
5858

5959

60+
.. decorator:: cached_method(func)
61+
cached_method(maxsize=None, typed=False)
62+
63+
Decorator to wrap a method with a bounded or unbounded cache.
64+
65+
When :func:`cache` or :func:`lru_cache` are used on an instance method, the
66+
instance (``self``) will be stored in the cache. As a result, instances cannot be
67+
garbage collected until the relevant caches are cleared.
68+
This decorator uses :func:`lru_cache`, but it wraps the unbound method to accept
69+
a weakref and ensures that caches are cleared when instances are garbage collected.
70+
71+
This is useful for expensive computations which are consistent with respect to
72+
an instance, for example, those which depend only on immutable attributes.
73+
74+
Example::
75+
76+
class DataSet:
77+
78+
def __init__(self, sequence_of_ints):
79+
self._data = tuple(sequence_of_ints)
80+
81+
@cached_method
82+
def shifted(self, shift):
83+
return DataSet([value + shift for value in self._data])
84+
85+
On instances, :func:`cached_method` behaves very similarly to :func:`cache`,
86+
providing :func:`!cache_info` and :func:`!cache_clear`.
87+
88+
:func:`cached_method` does not prevent all possible race conditions in
89+
multi-threaded usage. The function could run more than once on the
90+
same instance, with the same inputs, with the latest run setting the cached
91+
value. The per-instance cache is lazily initialized on first access (via the
92+
descriptor protocol), so parallel access on a single instance can race to
93+
initialize.
94+
95+
This decorator requires that the instance supports weak references.
96+
Some immutable types and slotted classes without ``__weakref__`` as one of
97+
the defined slots will encounter errors when the cached method is first used.
98+
99+
*maxsize* and *typed* are supported as keyword arguments to the decorator,
100+
and are passed to the underlying :func:`lru_cache`.
101+
102+
.. versionadded:: next
103+
104+
60105
.. decorator:: cached_property(func)
61106

62107
Transform a method of a class into a property whose value is computed once
@@ -122,51 +167,6 @@ The :mod:`!functools` module defines the following functions:
122167
Python 3.12+ this locking is removed.
123168

124169

125-
.. decorator:: cached_method(func)
126-
cached_method(maxsize=None, typed=False)
127-
128-
Decorator to wrap a method with a bounded or unbounded cache.
129-
130-
When :func:`cache` or :func:`lru_cache` are used on an instance method, the
131-
instance (``self``) will be stored in the cache. As a result, instances cannot be
132-
garbage collected until the relevant caches are cleared.
133-
This decorator uses :func:`lru_cache`, but it wraps the unbound method to accept
134-
a weakref and ensures that caches are cleared when instances are garbage collected.
135-
136-
This is useful for expensive computations which are consistent with respect to
137-
an instance, for example, those which depend only on immutable attributes.
138-
139-
Example::
140-
141-
class DataSet:
142-
143-
def __init__(self, sequence_of_ints):
144-
self._data = tuple(sequence_of_ints)
145-
146-
@cached_method
147-
def shifted(self, shift):
148-
return DataSet([value + shift for value in self._data])
149-
150-
On instances, :func:`cached_method` behaves very similarly to :func:`cache`,
151-
providing :func:`!cache_info` and :func:`!cache_clear`.
152-
153-
:func:`cached_method` does not prevent all possible race conditions in
154-
multi-threaded usage. The function could run more than once on the
155-
same instance, with the same inputs, with the latest run setting the cached
156-
value. The per-instance cache is lazily initialized on first access (via the
157-
descriptor protocol), so parallel access on a single instance can race to
158-
initialize.
159-
160-
This decorator requires that the instance supports weak references.
161-
Some immutable types and slotted classes without ``__weakref__`` as one of
162-
the defined slots will encounter errors when the cached method is first used.
163-
164-
*maxsize* and *typed* are supported as keyword arguments to the decorator,
165-
and are passed to the underlying :func:`lru_cache`.
166-
167-
.. versionadded:: next
168-
169-
170170
.. function:: cmp_to_key(func)
171171

172172
Transform an old-style comparison function to a :term:`key function`. Used

Lib/functools.py

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
__all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES',
1313
'total_ordering', 'cache', 'cmp_to_key', 'lru_cache', 'reduce',
1414
'partial', 'partialmethod', 'singledispatch', 'singledispatchmethod',
15-
'cached_property', 'Placeholder']
15+
'cached_method', 'cached_property', 'Placeholder']
1616

1717
from abc import get_cache_token
1818
from collections import namedtuple
@@ -1127,58 +1127,6 @@ def __wrapped__(self):
11271127
def register(self):
11281128
return self._unbound.register
11291129

1130-
1131-
################################################################################
1132-
### cached_property() - property result cached as instance attribute
1133-
################################################################################
1134-
1135-
_NOT_FOUND = object()
1136-
1137-
class cached_property:
1138-
def __init__(self, func):
1139-
self.func = func
1140-
self.attrname = None
1141-
self.__doc__ = func.__doc__
1142-
self.__module__ = func.__module__
1143-
1144-
def __set_name__(self, owner, name):
1145-
if self.attrname is None:
1146-
self.attrname = name
1147-
elif name != self.attrname:
1148-
raise TypeError(
1149-
"Cannot assign the same cached_property to two different names "
1150-
f"({self.attrname!r} and {name!r})."
1151-
)
1152-
1153-
def __get__(self, instance, owner=None):
1154-
if instance is None:
1155-
return self
1156-
if self.attrname is None:
1157-
raise TypeError(
1158-
"Cannot use cached_property instance without calling __set_name__ on it.")
1159-
try:
1160-
cache = instance.__dict__
1161-
except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
1162-
msg = (
1163-
f"No '__dict__' attribute on {type(instance).__name__!r} "
1164-
f"instance to cache {self.attrname!r} property."
1165-
)
1166-
raise TypeError(msg) from None
1167-
val = cache.get(self.attrname, _NOT_FOUND)
1168-
if val is _NOT_FOUND:
1169-
val = self.func(instance)
1170-
try:
1171-
cache[self.attrname] = val
1172-
except TypeError:
1173-
msg = (
1174-
f"The '__dict__' attribute on {type(instance).__name__!r} instance "
1175-
f"does not support item assignment for caching {self.attrname!r} property."
1176-
)
1177-
raise TypeError(msg) from None
1178-
return val
1179-
1180-
__class_getitem__ = classmethod(GenericAlias)
1181-
11821130
################################################################################
11831131
### cached_method -- a version of lru_cache() which uses id(self)
11841132
################################################################################
@@ -1255,3 +1203,55 @@ def decorator(func):
12551203
return decorator
12561204
else:
12571205
return _cached_method(func, maxsize=maxsize, typed=typed)
1206+
1207+
1208+
################################################################################
1209+
### cached_property() - property result cached as instance attribute
1210+
################################################################################
1211+
1212+
_NOT_FOUND = object()
1213+
1214+
class cached_property:
1215+
def __init__(self, func):
1216+
self.func = func
1217+
self.attrname = None
1218+
self.__doc__ = func.__doc__
1219+
self.__module__ = func.__module__
1220+
1221+
def __set_name__(self, owner, name):
1222+
if self.attrname is None:
1223+
self.attrname = name
1224+
elif name != self.attrname:
1225+
raise TypeError(
1226+
"Cannot assign the same cached_property to two different names "
1227+
f"({self.attrname!r} and {name!r})."
1228+
)
1229+
1230+
def __get__(self, instance, owner=None):
1231+
if instance is None:
1232+
return self
1233+
if self.attrname is None:
1234+
raise TypeError(
1235+
"Cannot use cached_property instance without calling __set_name__ on it.")
1236+
try:
1237+
cache = instance.__dict__
1238+
except AttributeError: # not all objects have __dict__ (e.g. class defines slots)
1239+
msg = (
1240+
f"No '__dict__' attribute on {type(instance).__name__!r} "
1241+
f"instance to cache {self.attrname!r} property."
1242+
)
1243+
raise TypeError(msg) from None
1244+
val = cache.get(self.attrname, _NOT_FOUND)
1245+
if val is _NOT_FOUND:
1246+
val = self.func(instance)
1247+
try:
1248+
cache[self.attrname] = val
1249+
except TypeError:
1250+
msg = (
1251+
f"The '__dict__' attribute on {type(instance).__name__!r} instance "
1252+
f"does not support item assignment for caching {self.attrname!r} property."
1253+
)
1254+
raise TypeError(msg) from None
1255+
return val
1256+
1257+
__class_getitem__ = classmethod(GenericAlias)

0 commit comments

Comments
 (0)