Factorials
Alex Martelli
aleax at aleax.it
Tue Sep 23 06:15:27 EDT 2003
Jacek Generowicz wrote:
...
> Or, you could let _any_ (keywordless) function benefit from its own
> experience:
This "_any_ (keywordless) function" is a substantial overbid. The
following implementation:
> # General memoizer: takes a function as returns another function which
> # has the same semantics as the original, but caches previously
> # calculated values
> def memoize(callable):
> cache = {}
> def proxy(*args):
> try: return cache[args]
> except KeyError: return cache.setdefault(args, callable(*args))
> return proxy
only works for a "PURE" function (e.g., not random.random() nor time.time(),
even though both ARE obviously part of the "any keywordless function"
set!-) *WITHOUT* unhashable arguments (e.g., not the new built-in 'sum',
even though it IS pure when called with a list argument -- because when
it tries to index cache with the list as part of the key, you'll get a
TypeError). Basically, it's fine if arguments (if any) are numbers,
strings, or hashable tuples, but it would be pushing your luck A LOT to
use it in any other case -- it's particularly dangerous, because you
will get a silent misbehavior, in a case such as:
class Foo:
def __init__(self, x=0, y=0):
self.x=x
self.y=y
def xplusy(somefoo):
return somefoo.x + somefoo.y
f = Foo()
print xplusy(f)
f.x = 23
print xplusy(f)
# so far so good, but now...:
xplusy = memoize(xplusy)
print xplusy(f)
# still LOOKS fine, but, check this out...:
f.y = 100
print xplusy(f)
# OOPS! xplusy's "memory" is now subtly incorrect...!!!
Unfortunately f "is hashable" (because it doesn't define a __cmp__)
but is NOT immutable, and xplusy, while pure, DOES depend on some
attributes of its (mutable) argument. The resulting behavior may
be rather unexpected, and, indeed, a subtle bug indeed to find...
Alex
More information about the Python-list
mailing list