@@ -1826,6 +1826,103 @@ For example, here is the implementation of
1826
1826
return True
1827
1827
return False
1828
1828
1829
+ How do I cache method calls?
1830
+ ----------------------------
1831
+
1832
+ The two principal tools for caching methods are
1833
+ :func: `functools.cached_property ` and :func: `functools.lru_cache `. The
1834
+ former stores results at the instance level and the latter at the class
1835
+ level.
1836
+
1837
+ The *cached_property * approach only works with methods that do not take
1838
+ any arguments. It does not create a reference to the instance. The
1839
+ cached method result will be kept only as long as the instance is alive.
1840
+
1841
+ The advantage is that when an instance is not longer used, the cached
1842
+ method result will be released right away. The disadvantage is that if
1843
+ instances accumulate, so too will the accumulated method results. They
1844
+ can grow without bound.
1845
+
1846
+ The *lru_cache * approach works with methods that have hashable
1847
+ arguments. It creates a reference to the instance unless special
1848
+ efforts are made to pass in weak references.
1849
+
1850
+ The advantage of the least recently used algorithm is that the cache is
1851
+ bounded by the specified *maxsize *. The disadvantage is that instances
1852
+ are kept alive until they age out of the cache or until the cache is
1853
+ cleared.
1854
+
1855
+ To avoid keeping an instance alive, it can be wrapped a weak reference
1856
+ proxy. That allows an instance to be freed prior aging out of the LRU
1857
+ cache. That said, the weak reference technique is rarely needed. It is
1858
+ only helpful when the instances hold large amounts of data and the
1859
+ normal aging-out process isn't fast enough. And even though the
1860
+ instance is released early, the cache still keeps references to the
1861
+ other method arguments and to the result of the method call.
1862
+
1863
+ This example shows the various techniques::
1864
+
1865
+ class Weather:
1866
+ "Lookup weather information on a government website"
1867
+
1868
+ def __init__(self, station_id):
1869
+ self._station_id = station_id
1870
+ # The _station_id is private and immutable
1871
+
1872
+ def current_temperature(self):
1873
+ "Latest hourly observation"
1874
+ # Do not cache this because old results
1875
+ # can be out of date.
1876
+
1877
+ @cached_property
1878
+ def location(self):
1879
+ "Return the longitude/latitude coordinates of the station"
1880
+ # Result only depends on the station_id
1881
+
1882
+ @lru_cache(maxsize=20)
1883
+ def historic_rainfall(self, date, units='mm'):
1884
+ "Rainfall on a given date"
1885
+ # Depends on the station_id, date, and units.
1886
+
1887
+ def climate(self, category='average_temperature'):
1888
+ "List of daily average temperatures for a full year"
1889
+ return self._climate(weakref.proxy(self), category)
1890
+
1891
+ @staticmethod
1892
+ @lru_cache(maxsize=10)
1893
+ def _climate(self_proxy, category):
1894
+ # Depends on a weak reference to the instance
1895
+ # and on the category parameter.
1896
+
1897
+ The above example assumes that the *station_id * never changes. If the
1898
+ relevant instance attributes are mutable, the *cached_property * approach
1899
+ can't be made to work because it cannot detect changes to the
1900
+ attributes.
1901
+
1902
+ The *lru_cache * approach can be made to work, but the class needs to define the
1903
+ *__eq__ * and *__hash__ * methods so the cache can detect relevant attribute
1904
+ updates::
1905
+
1906
+ class Weather:
1907
+ "Example with a mutable station identifier"
1908
+
1909
+ def __init__(self, station_id):
1910
+ self.station_id = station_id
1911
+
1912
+ def change_station(self, station_id):
1913
+ self.station_id = station_id
1914
+
1915
+ def __eq__(self, other):
1916
+ return self.station_id == other.station_id
1917
+
1918
+ def __hash__(self):
1919
+ return hash(self.station_id)
1920
+
1921
+ @lru_cache(maxsize=20)
1922
+ def historic_rainfall(self, date, units='cm'):
1923
+ 'Rainfall on a given date'
1924
+ # Depends on the station_id, date, and units.
1925
+
1829
1926
1830
1927
Modules
1831
1928
=======
0 commit comments