Following an answer from this previous question about memoizing all of a class’s methods by default, I’m using this metaclass:
from inspect import isfunction
class Immutable(type):
def __new__(cls, name, bases, dct):
for key, val in dct.items():
# Look only at methods/functions; ignore those with
# "special" names (starting with an underscore)
if isfunction(val) and val.__name__[0] != '_':
dct[key] = memoized(val)
elif hasattr(val, 'fget'):
# It's a property; we currently can't memoize these
pass
return type.__new__(cls, name, bases, dct)
This metaclass has the effect of applying the decorator memoized
to all of the functions and methods in a class. The only problem is that it doesn’t work with properties. (The classes that use this metaclass are treated as immutable, so I’m only worried about readonly properties here.) If I try to do something like
from time import sleep
class Foo(object):
__metaclass__ = Immutable
@property
def slow_calculation(self):
sleep(5) # simulate an intensive calculation
return 4
then slow_calculation
is not memoized—there’s a five-second delay each time it’s called, not just the first time.
The issue is that property()
returns a property
object, not a function, and so the isfunction
test in the metaclass’s __new__
won’t catch properties. As you can see I added a test that finds properties, but then I can’t figure out how to apply the memoized
decorator to the property getter fget
. I thought maybe I could say
elif hasattr(val, 'fget'):
val.fget = memoized(val.fget)
dct[key] = val
but in this case I get
Traceback (most recent call last):
[...]
File "utils.py", line 54, in __new__
val.fget = memoized(val.fget)
TypeError: Error when calling the metaclass bases
readonly attribute
Is there some other way to apply a decorator to a property getter?