mirror of
https://github.com/python/cpython.git
synced 2025-11-01 10:45:30 +00:00
bpo-44310: Add a FAQ entry for caching method calls (GH-26731)
This commit is contained in:
parent
f73377d57c
commit
7f01f77f8f
1 changed files with 97 additions and 0 deletions
|
|
@ -1826,6 +1826,103 @@ For example, here is the implementation of
|
|||
return True
|
||||
return False
|
||||
|
||||
How do I cache method calls?
|
||||
----------------------------
|
||||
|
||||
The two principal tools for caching methods are
|
||||
:func:`functools.cached_property` and :func:`functools.lru_cache`. The
|
||||
former stores results at the instance level and the latter at the class
|
||||
level.
|
||||
|
||||
The *cached_property* approach only works with methods that do not take
|
||||
any arguments. It does not create a reference to the instance. The
|
||||
cached method result will be kept only as long as the instance is alive.
|
||||
|
||||
The advantage is that when an instance is not longer used, the cached
|
||||
method result will be released right away. The disadvantage is that if
|
||||
instances accumulate, so too will the accumulated method results. They
|
||||
can grow without bound.
|
||||
|
||||
The *lru_cache* approach works with methods that have hashable
|
||||
arguments. It creates a reference to the instance unless special
|
||||
efforts are made to pass in weak references.
|
||||
|
||||
The advantage of the least recently used algorithm is that the cache is
|
||||
bounded by the specified *maxsize*. The disadvantage is that instances
|
||||
are kept alive until they age out of the cache or until the cache is
|
||||
cleared.
|
||||
|
||||
To avoid keeping an instance alive, it can be wrapped a weak reference
|
||||
proxy. That allows an instance to be freed prior aging out of the LRU
|
||||
cache. That said, the weak reference technique is rarely needed. It is
|
||||
only helpful when the instances hold large amounts of data and the
|
||||
normal aging-out process isn't fast enough. And even though the
|
||||
instance is released early, the cache still keeps references to the
|
||||
other method arguments and to the result of the method call.
|
||||
|
||||
This example shows the various techniques::
|
||||
|
||||
class Weather:
|
||||
"Lookup weather information on a government website"
|
||||
|
||||
def __init__(self, station_id):
|
||||
self._station_id = station_id
|
||||
# The _station_id is private and immutable
|
||||
|
||||
def current_temperature(self):
|
||||
"Latest hourly observation"
|
||||
# Do not cache this because old results
|
||||
# can be out of date.
|
||||
|
||||
@cached_property
|
||||
def location(self):
|
||||
"Return the longitude/latitude coordinates of the station"
|
||||
# Result only depends on the station_id
|
||||
|
||||
@lru_cache(maxsize=20)
|
||||
def historic_rainfall(self, date, units='mm'):
|
||||
"Rainfall on a given date"
|
||||
# Depends on the station_id, date, and units.
|
||||
|
||||
def climate(self, category='average_temperature'):
|
||||
"List of daily average temperatures for a full year"
|
||||
return self._climate(weakref.proxy(self), category)
|
||||
|
||||
@staticmethod
|
||||
@lru_cache(maxsize=10)
|
||||
def _climate(self_proxy, category):
|
||||
# Depends on a weak reference to the instance
|
||||
# and on the category parameter.
|
||||
|
||||
The above example assumes that the *station_id* never changes. If the
|
||||
relevant instance attributes are mutable, the *cached_property* approach
|
||||
can't be made to work because it cannot detect changes to the
|
||||
attributes.
|
||||
|
||||
The *lru_cache* approach can be made to work, but the class needs to define the
|
||||
*__eq__* and *__hash__* methods so the cache can detect relevant attribute
|
||||
updates::
|
||||
|
||||
class Weather:
|
||||
"Example with a mutable station identifier"
|
||||
|
||||
def __init__(self, station_id):
|
||||
self.station_id = station_id
|
||||
|
||||
def change_station(self, station_id):
|
||||
self.station_id = station_id
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.station_id == other.station_id
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.station_id)
|
||||
|
||||
@lru_cache(maxsize=20)
|
||||
def historic_rainfall(self, date, units='cm'):
|
||||
'Rainfall on a given date'
|
||||
# Depends on the station_id, date, and units.
|
||||
|
||||
|
||||
Modules
|
||||
=======
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue