Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f309828

Browse files
committed
Remove the lfu_cache. Add more tests.
1 parent 0f56e90 commit f309828

3 files changed

Lines changed: 30 additions & 130 deletions

File tree

Doc/whatsnew/3.2.rst

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -66,45 +66,32 @@ Some smaller changes made to the core Python language are:
6666
New, Improved, and Deprecated Modules
6767
=====================================
6868

69-
* The functools module now includes two new decorators for caching function
70-
calls, :func:`functools.lru_cache` and :func:`functools.lfu_cache`. These can
71-
save repeated queries to an external resource whenever the results are
72-
expected to be the same.
69+
* The functools module now includes a new decorator for caching function calls.
70+
:func:`functools.lru_cache` can save repeated queries to an external resource
71+
whenever the results are expected to be the same.
7372

7473
For example, adding a caching decorator to a database query function can save
7574
database accesses for popular searches::
7675

77-
@functools.lfu_cache(maxsize=50)
76+
@functools.lru_cache(maxsize=300)
7877
def get_phone_number(name):
7978
c = conn.cursor()
8079
c.execute('SELECT phonenumber FROM phonelist WHERE name=?', (name,))
8180
return c.fetchone()[0]
8281

83-
The caches support two strategies for limiting their size to *maxsize*. The
84-
LFU (least-frequently-used) cache works bests when popular queries remain the
85-
same over time. In contrast, the LRU (least-recently-used) cache works best
86-
query popularity changes over time (for example, the most popular news
87-
articles change each day as newer articles are added).
88-
89-
The two caching decorators can be composed (nested) to handle hybrid cases.
90-
For example, music searches can reflect both long-term patterns (popular
91-
classics) and short-term trends (new releases)::
92-
93-
@functools.lfu_cache(maxsize=500)
94-
@functools.lru_cache(maxsize=100)
95-
def find_lyrics(song):
96-
query = 'http://www.example.com/songlist/%s' % urllib.quote(song)
97-
page = urllib.urlopen(query).read()
98-
return parse_lyrics(page)
99-
100-
To help with choosing an effective cache size, the wrapped function
101-
is instrumented with two attributes *hits* and *misses*::
102-
103-
>>> for song in user_requests:
104-
... find_lyrics(song)
105-
>>> print(find_lyrics.hits, find_lyrics.misses)
82+
To help with choosing an effective cache size, the wrapped function is
83+
instrumented with two attributes *hits* and *misses*::
84+
85+
>>> for name in user_requests:
86+
... get_phone_number(name)
87+
>>> print(get_phone_number.hits, get_phone_number.misses)
10688
4805 980
10789

90+
If the phonelist table gets updated, the outdated contents of the cache can be
91+
cleared with::
92+
93+
>>> get_phone_number.clear()
94+
10895
(Contributed by Raymond Hettinger)
10996

11097
* The previously deprecated :func:`contextlib.nested` function has been

Lib/functools.py

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -110,58 +110,6 @@ def __hash__(self):
110110
raise TypeError('hash not implemented')
111111
return K
112112

113-
def lfu_cache(maxsize=100):
114-
"""Least-frequently-used cache decorator.
115-
116-
Arguments to the cached function must be hashable.
117-
Cache performance statistics stored in f.hits and f.misses.
118-
Clear the cache using f.clear().
119-
http://en.wikipedia.org/wiki/Cache_algorithms#Least-Frequently_Used
120-
121-
"""
122-
def decorating_function(user_function, tuple=tuple, sorted=sorted,
123-
len=len, KeyError=KeyError):
124-
cache = {} # mapping of args to results
125-
use_count = Counter() # times each key has been accessed
126-
kwd_mark = object() # separate positional and keyword args
127-
lock = Lock()
128-
129-
@wraps(user_function)
130-
def wrapper(*args, **kwds):
131-
key = args
132-
if kwds:
133-
key += (kwd_mark,) + tuple(sorted(kwds.items()))
134-
try:
135-
with lock:
136-
use_count[key] += 1 # count a use of this key
137-
result = cache[key]
138-
wrapper.hits += 1
139-
except KeyError:
140-
result = user_function(*args, **kwds)
141-
with lock:
142-
use_count[key] += 1 # count a use of this key
143-
cache[key] = result
144-
wrapper.misses += 1
145-
if len(cache) > maxsize:
146-
# purge the 10% least frequently used entries
147-
for key, _ in nsmallest(maxsize // 10 or 1,
148-
use_count.items(),
149-
key=itemgetter(1)):
150-
del cache[key], use_count[key]
151-
return result
152-
153-
def clear():
154-
"""Clear the cache and cache statistics"""
155-
with lock:
156-
cache.clear()
157-
use_count.clear()
158-
wrapper.hits = wrapper.misses = 0
159-
160-
wrapper.hits = wrapper.misses = 0
161-
wrapper.clear = clear
162-
return wrapper
163-
return decorating_function
164-
165113
def lru_cache(maxsize=100):
166114
"""Least-recently-used cache decorator.
167115

Lib/test/test_functools.py

Lines changed: 15 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -483,73 +483,38 @@ def orig(x, y):
483483
self.assertEqual(f.misses, 1)
484484

485485
# test size zero (which means "never-cache")
486-
f_cnt = 0
487486
@functools.lru_cache(0)
488487
def f():
489488
nonlocal f_cnt
490489
f_cnt += 1
491490
return 20
492-
self.assertEqual(f(), 20)
493-
self.assertEqual(f(), 20)
494-
self.assertEqual(f(), 20)
495-
self.assertEqual(f_cnt, 3)
491+
f_cnt = 0
492+
for i in range(5):
493+
self.assertEqual(f(), 20)
494+
self.assertEqual(f_cnt, 5)
496495

497496
# test size one
498-
f_cnt = 0
499497
@functools.lru_cache(1)
500498
def f():
501499
nonlocal f_cnt
502500
f_cnt += 1
503501
return 20
504-
self.assertEqual(f(), 20)
505-
self.assertEqual(f(), 20)
506-
self.assertEqual(f(), 20)
502+
f_cnt = 0
503+
for i in range(5):
504+
self.assertEqual(f(), 20)
507505
self.assertEqual(f_cnt, 1)
508506

509-
def test_lfu(self):
510-
def orig(x, y):
511-
return 3*x+y
512-
f = functools.lfu_cache(maxsize=20)(orig)
513-
514-
domain = range(5)
515-
for i in range(1000):
516-
x, y = choice(domain), choice(domain)
517-
actual = f(x, y)
518-
expected = orig(x, y)
519-
self.assertEquals(actual, expected)
520-
self.assert_(f.hits > f.misses)
521-
self.assertEquals(f.hits + f.misses, 1000)
522-
523-
f.clear() # test clearing
524-
self.assertEqual(f.hits, 0)
525-
self.assertEqual(f.misses, 0)
526-
f(x, y)
527-
self.assertEqual(f.hits, 0)
528-
self.assertEqual(f.misses, 1)
529-
530-
# test size zero (which means "never-cache")
531-
f_cnt = 0
532-
@functools.lfu_cache(0)
533-
def f():
507+
# test size two
508+
@functools.lru_cache(2)
509+
def f(x):
534510
nonlocal f_cnt
535511
f_cnt += 1
536-
return 20
537-
self.assertEqual(f(), 20)
538-
self.assertEqual(f(), 20)
539-
self.assertEqual(f(), 20)
540-
self.assertEqual(f_cnt, 3)
541-
542-
# test size one
512+
return x*10
543513
f_cnt = 0
544-
@functools.lfu_cache(1)
545-
def f():
546-
nonlocal f_cnt
547-
f_cnt += 1
548-
return 20
549-
self.assertEqual(f(), 20)
550-
self.assertEqual(f(), 20)
551-
self.assertEqual(f(), 20)
552-
self.assertEqual(f_cnt, 1)
514+
for x in 7, 9, 7, 9, 7, 9, 8, 8, 8, 9, 9, 9, 8, 8, 8, 7:
515+
# * * * *
516+
self.assertEqual(f(x), x*10)
517+
self.assertEqual(f_cnt, 4)
553518

554519
def test_main(verbose=None):
555520
test_classes = (

0 commit comments

Comments
 (0)