Descriptors and side effects

Rich Harkins rich at worldsinfinite.com
Mon Nov 5 18:42:53 CET 2007


Bruno Desthuilliers wrote:
> Rich Harkins a écrit :
>> mrkafk at gmail.com wrote:
>>> Hello everyone,
>>>
>>> I'm trying to do seemingly trivial thing with descriptors: have
>>> another attribute updated on dot access in object defined using
>>> descriptors.
>> [snip]
>>
>>> A setter function should have updated self.l just like it updated
>>> self.s:
>>>
>>> 	def __set__(self, obj, val):
>>> 		self.s=val
>>> 		self.l=len(val)
>>> 		print "setting value:", self.s, "length:", self.l
>>>
>>> Yet it didn't happen.
>>>
>> [snip]
>>
>> I noticed that Python will block all attribute overrides (either via
>> __dict__ through setattr) if the property has a __set__ method. 
> 
> It doesn't "block", it controls access to... Of course, if the __set__ 
> method is a no-op, then nothing will happen.
> 
>> The
>> standard property has this method and there is no way that I can find to
>> defeat it.
> 
> "defeat" ? Why don't you just pass the appropriate fset function to 
> property ?
> 
>>  So, here is what I use:
>>
>> class ConstProperty(object):
>>     """
>>     Provides a property that keeps its return value.  The function will
>>     only be called on the first access.  After that the same value can
>>     be used over and over again with no function call penalty.  If the
>>     cached value needs to be cleared, simply del the attribute.
>>
>>     >>> class MyClass(object):
>>     ...     def __init__(self, x):
>>     ...         self.x = x
>>     ...     @ConstProperty
>>     ...     def y(self):
>>     ...         print "HERE"
>>     ...         return self.x ** 2
>>     ...
>>     >>> obj = MyClass(5)
>>     >>> obj.y
>>     HERE
>>     25
>>     >>> obj.y
>>     25
>>     """
>>
>>     def __init__(self, fn):
>>         self.fn = fn
>>
>>     def __get__(self, target, cls=None):
>>         if target is None:
>>             return self.fn		# Helps pydoc
>>         else:
>>             obj = self.fn(target)
>>             setattr(target, self.fn.__name__, obj)
>>             return obj
> 
> 
> 
>  >>> m = MyClass(5)
>  >>> m.__dict__
> {'x': 5}
>  >>> m.y
> HERE
> 25
>  >>> m.__dict__
> {'y': 25, 'x': 5}
>  >>> m.x = 42
>  >>> m.y
> 25
>  >>> m.__dict__
> {'y': 25, 'x': 42}
>  >>>
> 
> 
> I'm sorry, but this looks like a very complicated way to do a simple thing:
> 
> class MySimpleClass(object):
>    def __init__(self, x):
>      self.x = x
>      self.y = x ** 2
> 
> 




More information about the Python-list mailing list