Prev: why is there now execption for windows? trying to listen twice tothe same port
Next: [ANN] Pyspread 0.1 released
From: Bruno Desthuilliers on 22 Mar 2010 11:44 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 22 Mar 2010 13:36 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 22 Mar 2010 18:16 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 23 Mar 2010 05:13 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.
First
|
Prev
|
Pages: 1 2 3 4 Prev: why is there now execption for windows? trying to listen twice tothe same port Next: [ANN] Pyspread 0.1 released |