How to create attributes with 'computed at first use' values.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | #
# first solution:
#
class sample(object):
class one(object):
def __get__(self, obj, type=None):
print "computing ..."
obj.one = 1
return 1
one = one()
x=sample()
print x.one
print x.one
#
# other solution:
#
# lazy attribute descriptor
class lazyattr(object):
def __init__(self, fget, doc=''):
self.fget = fget
self.__doc__ = doc
def __appoint__(self, name, cl_name):
if hasattr(self,"name"):
raise SyntaxError, "conflict between "+name+" and "+self.name
self.name = name
def __get__(self, obj, cl=None):
if obj is None:
return self
value = self.fget(obj)
setattr(obj, self.name, value)
return value
# appointer metaclass:
# call the members __appoint__ method
class appointer(type):
def __init__(self, cl_name, bases, namespace):
for name,obj in namespace.iteritems():
try:
obj.__appoint__(name, cl_name)
except AttributeError:
pass
super(appointer, self).__init__(cl_name, bases, namespace)
# base class for lazyattr users
class lazyuser(object):
__metaclass__ = appointer
# usage sample
class sample(lazyuser):
def one(self):
print "computing ..."
return 1
one = lazyattr(one, "one lazyattr")
x=sample()
print x.one
print x.one
del x.one
print x.one
|
This recipe works only for Python >= 2.2.
When an attribute is expensive to compute, it's sometime desirable to delay this computing until the value is actually needed.
The first solution show how to create a lazy attribute "by hand"
The second solution allow to create this kind of attributes with a syntax similar to property attributes definition. In the second solution, lazyattr user classes must inherit from lazyuser. This is needed for the lazyattr __appoint__ initialization.
I hope that this recipe can also be a good sample of metaclass and descriptor definitions.
clever! This is an usage of descriptors I have never thought about ;)