From: Bruno Desthuilliers on
kj a �crit :
> In <mailman.1030.1269194878.23598.python-list(a)python.org> Dennis Lee Bieber <wlfraed(a)ix.netcom.com> writes:
>
>> On Sun, 21 Mar 2010 16:57:40 +0000 (UTC), kj <no.email(a)please.post>
>> declaimed the following in gmane.comp.python.general:
>
>>> Regarding properties, is there a built-in way to memoize them? For
>>> example, suppose that the value of a property is obtained by parsing
>>> the contents of a file (specified in another instance attribute).
>>> It would make no sense to do this parsing more than once. Is there
>>> a standard idiom for memoizing the value once it is determined for
>>> the first time?
>>>
>> Pickle, Shelve? Maybe in conjunction with SQLite3...
>
> I was thinking of something less persistent; in-memory, that is.
> Maybe something in the spirit of:
>
> @property
> def foo(self):
> # up for some "adaptive auto-redefinition"?
> self.foo = self._some_time_consuming_operation()
> return self.foo
>
> ...except that that assignment won't work! It bombs with "AttributeError:
> can't set attribute".
>
> ~K
>
> PS: BTW, this is not the first time that attempting to set an
> attribute (in a class written by me even) blows up on me. It's
> situations like these that rattle my grasp of attributes, hence my
> original question about boring, plodding, verbose Java-oid accessors.
> For me these Python attributes are still waaay too mysterious and
> unpredictable to rely on.

Somehow simplified, here's what you have to know:

1/ there are instance attributes and class attributes. Instance
attributes lives in the instance's __dict__, class attributes lives in
the class's __dict__ or in a parent's class __dict__.

2/ when looking up an attribute on an instance, the rules are
* first, check if there's a key by that name in the instance's __dict__.
If yes, return the associated value
* else, check if there's a class or parent class attribute by that name.
* if yes
** if the attribute has a '__get__' method, call the __get__ method with
class and instance as arguments, and return the result (this is known as
the "descriptor protocol" and provides support for computed attributes
(including methods and properties)
** else return the attribute itself
* else (if nothing has been found yet), look for a __getattr__ method in
the class and it's parents. If found, call this __getattr__ method with
the attribute name and return the result
* else, give up and raise an AttributeError

3/ When binding an attribute on an instance, the rules are:
* first, check if there's a class (or parent class) attribute by that
name that has a '__set__' method. If yes, call this class attribute's
__set__ method with instance and value as arguments. This is the second
part part of the "descriptor protocol", as used by the property type.
* else, add the attribute's name and value in the instance's __dict__


As I said, this is a somehow simplified description of the process - I
skipped the parts about __slots__, __getattribute__ and __setattr__, as
well as the part about how function class attributes become methods. But
this should be enough to get an idea of what's going on.

In your above case, you defined a "foo" property class attribute. The
property type implements both __get__ and __set__, but you only defined
a callback for the __get__ method (the function you decorated with
'property'), so when you try to rebind "foo", the default property
type's __set__ implementation is invoked, which behaviour is to forbid
setting the attribute. If you want a settable property, you have to
provide a setter too.

Now if you want a "replaceable" property-like attribute, you could
define your own computed attribute (aka "descriptor") type _without_ a
__set__ method:

class replaceableprop(object):
def __init__(self, fget):
self._fget = fget
def __get__(self, instance, cls):
if instance is None:
return self
return self._fget(instance)

@replaceableprop
def foo(self):
# will add 'foo' into self.__dict__, s
self.foo = self._some_time_consuming_operation()
return self.foo


Another (better IMHO) solution is to use a plain property, and store the
computed value as an implementation attribute :

@property
def foo(self):
cached = self.__dict__.get('_foo_cache')
if cached is None:
self._foo_cache = cached = self._some_time_consuming_operation()
return cached


> Sometimes one can set them, sometimes
> not, and I can't quite tell the two situations apart. It's all
> very confusing to the Noob. (I'm sure this is all documented
> *somewhere*, but this does not make using attributes any more
> intuitive or straightforward. I'm also sure that *eventually*,
> with enough Python experience under one's belt, this all becomes
> second nature. My point is that Python attributes are not as
> transparent and natural to the uninitiated as some of you folks
> seem to think.)

I agree that the introduction of the descriptor protocol added some more
complexity to an already somehow unusual model object.

HTH.
From: John Posner on
On 3/22/2010 11:44 AM, Bruno Desthuilliers wrote:

<snip>

> Another (better IMHO) solution is to use a plain property, and store the
> computed value as an implementation attribute :
>
> @property
> def foo(self):
> cached = self.__dict__.get('_foo_cache')
> if cached is None:
> self._foo_cache = cached = self._some_time_consuming_operation()
> return cached
>

There's no need to access __dict__ directly. I believe this is
equivalent (and clearer):

@property
def foo(self):
try:
cached = self._foo_cache
except AttributeError:
self._foo_cache = cached = self._time_consuming_op()
return cached

-John
From: kj on
In <4ba79040$0$22397$426a74cc(a)news.free.fr> Bruno Desthuilliers <bruno.42.desthuilliers(a)websiteburo.invalid> writes:

>kj a �crit :
>> PS: BTW, this is not the first time that attempting to set an
>> attribute (in a class written by me even) blows up on me. It's
>> situations like these that rattle my grasp of attributes, hence my
>> original question about boring, plodding, verbose Java-oid accessors.
>> For me these Python attributes are still waaay too mysterious and
>> unpredictable to rely on.

>Somehow simplified, here's what you have to know:

....

>As I said, this is a somehow simplified description of the process - I
>skipped the parts about __slots__, __getattribute__ and __setattr__, as
>well as the part about how function class attributes become methods.
>this should be enough to get an idea of what's going on.


Thank you, sir! That was quite the education.

(Someday I really should read carefully the official documentation
for the stuff you described, assuming it exists.)

Thanks also for your code suggestions.

~K
From: Bruno Desthuilliers on
John Posner a �crit :
> On 3/22/2010 11:44 AM, Bruno Desthuilliers wrote:
>
> <snip>
>
>> Another (better IMHO) solution is to use a plain property, and store the
>> computed value as an implementation attribute :
>>
>> @property
>> def foo(self):
>> cached = self.__dict__.get('_foo_cache')
>> if cached is None:
>> self._foo_cache = cached = self._some_time_consuming_operation()
>> return cached
>>
>
> There's no need to access __dict__ directly.

Nope, inded. I guess I wrote it that way to make clear that we were
looking for an instance attribute (as a sequel of my previous writing on
attribute lookup rules).

> I believe this is
> equivalent (and clearer):
>
> @property
> def foo(self):
> try:
> cached = self._foo_cache
> except AttributeError:
> self._foo_cache = cached = self._time_consuming_op()
> return cached
>

This is functionally _almost_ equivalent - won't work the same if
there's a class attribute "_foo_cache", which might or not be a good
thing !-)

Will possibly be a bit faster after the first access - IIRC setting up
an error handler is by itself cheaper than doing a couple attribute
access and a method call - but I'd timeit before worrying about it.