Descriptors and side effects

Bruno Desthuilliers bruno.42.desthuilliers at wtf.websiteburo.oops.com
Mon Nov 5 17:28:13 CET 2007


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