Thanks Steve - actually my question was simpler than that. I just wanted to use Daniels' recipe of lazy initialization on objects with __slots__:
class Lazy(object): def __init__(self, calculate_function): self._calculate = calculate_function
def __get__(self, obj, _=None): if obj is None: return self value = self._calculate(obj) setattr(obj, self._calculate.func_name, value) return value
class SomeClass(object):
__slots__ = 'someprop'
@Lazy def someprop(self): print 'Actually calculating value' return 13
o = SomeClass() print o.someprop print o.someprop
Running the code above will produce:
Actually calculating value Traceback (most recent call last): File "Lazy.py", line 26, in ? print o.someprop File "Lazy.py", line 11, in __get__ setattr(obj, self._calculate.func_name, value) AttributeError: 'SomeClass' object attribute 'someprop' is read-only
Removing the __slots__ statement, everything would run normally.
Is there any workaround?
Hmm... Well, there's a thread on a similar topic:
http://mail.python.org/pipermail/python-dev/2003-May/035575.html
The summary is basically that '__slots__' creates descriptors for all the names in the __slots__ iterable. If you define a function (or a Lazy object) at the class level with the same name as a slot, it replaces the descriptor for that slot name. So then when setattr gets called, it can't find the descriptor anymore, so it can't write the value...
One workaround would be to have Lazy store the values instead of setting the attribute on the instance -- something like:
py> class Lazy(object): ... def __init__(self, calculate_function): ... self._calculate = calculate_function ... self._values = {} ... def __get__(self, obj, _=None): ... if obj is None: ... return self ... obj_id = id(obj) ... if obj_id not in self._values: ... self._values[obj_id] = self._calculate(obj) ... return self._values[obj_id] ... py> py> class SomeClass(object): ... __slots__ = [] ... @Lazy ... def someprop(self): ... print 'Actually calculating value' ... return 13 ... py> o = SomeClass() py> o.someprop Actually calculating value 13 py> o.someprop 13
Basically Lazy just stores a mapping from object ids to calculated values. Not as pretty as the original solution, but if you have to have __slots__ defined[1], I guess this might work.
Note that I don't need to declare any slots since the someprop descriptor is a class-level attribute, not an instance-level one...
Steve
[1] Are you sure you have to use __slots__? Unless you're creating a very large number of these objects, you probably won't notice the difference in memory consumption...
--
http://mail.python.org/mailman/listinfo/python-list