Issue 13227: Option to make the lru_cache() type specific (suggested by Andrew Koenig).
diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst
index 2316e80..4eaf54e 100644
--- a/Doc/library/functools.rst
+++ b/Doc/library/functools.rst
@@ -40,7 +40,7 @@
    .. versionadded:: 3.2
 
 
-.. decorator:: lru_cache(maxsize=100)
+.. decorator:: lru_cache(maxsize=100, typed=False)
 
    Decorator to wrap a function with a memoizing callable that saves up to the
    *maxsize* most recent calls.  It can save time when an expensive or I/O bound
@@ -52,6 +52,10 @@
    If *maxsize* is set to None, the LRU feature is disabled and the cache
    can grow without bound.
 
+   If *typed* is set to True, function arguments of different types will be
+   cached separately.  For example, ``f(3)`` and ``f(3.0)`` will be treated
+   as distinct calls with distinct results.
+
    To help measure the effectiveness of the cache and tune the *maxsize*
    parameter, the wrapped function is instrumented with a :func:`cache_info`
    function that returns a :term:`named tuple` showing *hits*, *misses*,
@@ -67,8 +71,8 @@
 
    An `LRU (least recently used) cache
    <http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used>`_ works
-   best when more recent calls are the best predictors of upcoming calls (for
-   example, the most popular articles on a news server tend to change daily).
+   best when the most recent calls are the best predictors of upcoming calls (for
+   example, the most popular articles on a news server tend to change each day).
    The cache's size limit assures that the cache does not grow without bound on
    long-running processes such as web servers.
 
@@ -111,6 +115,9 @@
 
    .. versionadded:: 3.2
 
+   .. versionchanged:: 3.3
+      Added the *typed* option.
+
 .. decorator:: total_ordering
 
    Given a class defining one or more rich comparison ordering methods, this
diff --git a/Lib/functools.py b/Lib/functools.py
index 038f284..1abb37a 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -121,12 +121,16 @@
 
 _CacheInfo = namedtuple("CacheInfo", "hits misses maxsize currsize")
 
-def lru_cache(maxsize=100):
+def lru_cache(maxsize=100, typed=False):
     """Least-recently-used cache decorator.
 
     If *maxsize* is set to None, the LRU features are disabled and the cache
     can grow without bound.
 
+    If *typed* is True, arguments of different types will be cached separately.
+    For example, f(3.0) and f(3) will be treated as distinct calls with
+    distinct results.
+
     Arguments to the cached function must be hashable.
 
     View the cache statistics named tuple (hits, misses, maxsize, currsize) with
@@ -142,7 +146,7 @@
     # to allow the implementation to change (including a possible C version).
 
     def decorating_function(user_function,
-                tuple=tuple, sorted=sorted, len=len, KeyError=KeyError):
+            *, tuple=tuple, sorted=sorted, map=map, len=len, type=type, KeyError=KeyError):
 
         hits = misses = 0
         kwd_mark = (object(),)          # separates positional and keyword args
@@ -156,7 +160,12 @@
                 nonlocal hits, misses
                 key = args
                 if kwds:
-                    key += kwd_mark + tuple(sorted(kwds.items()))
+                    sorted_items = tuple(sorted(kwds.items()))
+                    key += kwd_mark + sorted_items
+                if typed:
+                    key += tuple(map(type, args))
+                    if kwds:
+                        key += tuple(type(v) for k, v in sorted_items)
                 try:
                     result = cache[key]
                     hits += 1
@@ -177,7 +186,12 @@
                 nonlocal hits, misses
                 key = args
                 if kwds:
-                    key += kwd_mark + tuple(sorted(kwds.items()))
+                    sorted_items = tuple(sorted(kwds.items()))
+                    key += kwd_mark + sorted_items
+                if typed:
+                    key += tuple(map(type, args))
+                    if kwds:
+                        key += tuple(type(v) for k, v in sorted_items)
                 with lock:
                     try:
                         result = cache[key]
diff --git a/Lib/re.py b/Lib/re.py
index cdf5976..4b90b3f 100644
--- a/Lib/re.py
+++ b/Lib/re.py
@@ -207,7 +207,7 @@
 
 def purge():
     "Clear the regular expression caches"
-    _compile_typed.cache_clear()
+    _compile.cache_clear()
     _compile_repl.cache_clear()
 
 def template(pattern, flags=0):
@@ -253,11 +253,8 @@
 
 _pattern_type = type(sre_compile.compile("", 0))
 
+@functools.lru_cache(maxsize=500, typed=True)
 def _compile(pattern, flags):
-    return _compile_typed(type(pattern), pattern, flags)
-
-@functools.lru_cache(maxsize=500)
-def _compile_typed(text_bytes_type, pattern, flags):
     # internal: compile pattern
     if isinstance(pattern, _pattern_type):
         if flags:
diff --git a/Lib/test/test_functools.py b/Lib/test/test_functools.py
index a31c92e..c4d9fe6 100644
--- a/Lib/test/test_functools.py
+++ b/Lib/test/test_functools.py
@@ -734,6 +734,22 @@
             with self.assertRaises(IndexError):
                 func(15)
 
+    def test_lru_with_types(self):
+        for maxsize in (None, 100):
+            @functools.lru_cache(maxsize=maxsize, typed=True)
+            def square(x):
+                return x * x
+            self.assertEqual(square(3), 9)
+            self.assertEqual(type(square(3)), type(9))
+            self.assertEqual(square(3.0), 9.0)
+            self.assertEqual(type(square(3.0)), type(9.0))
+            self.assertEqual(square(x=3), 9)
+            self.assertEqual(type(square(x=3)), type(9))
+            self.assertEqual(square(x=3.0), 9.0)
+            self.assertEqual(type(square(x=3.0)), type(9.0))
+            self.assertEqual(square.cache_info().hits, 4)
+            self.assertEqual(square.cache_info().misses, 4)
+
 def test_main(verbose=None):
     test_classes = (
         TestPartial,
diff --git a/Misc/NEWS b/Misc/NEWS
index cd6747e..3fe419b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -319,6 +319,9 @@
 Library
 -------
 
+- Issue #13227: functools.lru_cache() now has a option to distinguish
+  calls with different argument types.
+
 - Issue #6090: zipfile raises a ValueError when a document with a timestamp
   earlier than 1980 is provided. Patch contributed by Petri Lehtinen.