diff --git a/pytools/__init__.py b/pytools/__init__.py
index 9feae0112b242b47f61559e2e4b55cd88aa46e29..6cd3205a7d49a8ee33ddda229c04d2348a23f1d8 100644
--- a/pytools/__init__.py
+++ b/pytools/__init__.py
@@ -370,14 +370,32 @@ def single_valued(iterable, equality_pred=operator.eq):
 # {{{ memoization / attribute storage
 
 def memoize(*args, **kwargs):
+    """Stores previously computed function values in a cache.
+
+    Two keyword-only arguments are supported:
+
+    :arg use_kwargs: Allows the caller to use keyword arguments. Defaults to
+        ``False``. Setting this to ``True`` has a non-negligible performance
+        impact.
+    :arg key: A function receiving the same arguments as the decorated function
+        which computes and returns the cache key.
+    """
+
     use_kw = bool(kwargs.pop('use_kwargs', False))
-    key_func = kwargs.pop(
-        'key', ((lambda *a, **kw: (a, frozenset(kw.iteritems())))
-                if use_kw else None))
+
+    if use_kw:
+        def default_key_func(*inner_args, **inner_kwargs):
+            return inner_args, frozenset(inner_kwargs.iteritems())
+    else:
+        default_key_func = None
+
+    key_func = kwargs.pop("key", default_key_func)
+
     if kwargs:
         raise TypeError(
-            "memorize recived unexpected keyword arguments: %s"
+            "memoize received unexpected keyword arguments: %s"
             % ", ".join(kwargs.keys()))
+
     if key_func is not None:
         @my_decorator
         def _deco(func, *args, **kwargs):
@@ -416,7 +434,7 @@ def memoize(*args, **kwargs):
     if callable(args[0]) and len(args) == 1:
         return _deco(args[0])
     raise TypeError(
-        "memorize recived unexpected position arguments: %s" % args)
+        "memoize received unexpected position arguments: %s" % args)
 
 FunctionValueCache = memoize