[Python-Dev] Computed-attributes

Gordon McMillan gmcm@hypernet.com
Thu, 20 Jul 2000 13:03:13 -0400


Paul Prescod wrote:

> Gordon McMillan wrote:
> > 
> > What happens here (and when):
> > class A:
> >   y = None
> > class B(A):
> >   def __XXX_y__(self):
> >      ...
> 
> I would say that the latter would just take precedence over the
> former as if you had typed:
> 
> class B(A):
>     __XXX_y__=ComputedAttribute( foo, bar, baz )

OK. I gather that the ComputedAttribute is actually named 
after the attribute. So in this situation, "y" is found in 
B.__dict__, and is a ComputedAttribute which hides A's 
(normal) attribute. Then:

class A:
  def __init__(self):
     self.y = <something>
class B(A):
  def __set_y__(self, value):
     ...
  def __init__(self):
    A.__init__(self)

means that the hook in B will be invoked when A.__init__ 
runs. I wonder what "gotchas" lie in wait ;-).
 
> > Hmmm. Current Python: search for y fails, try __getattr__.
> > Proposed: search for y fails, try the __get_y__, then
> > __getattr__. We've still done the same work before trying the
> > hook, so how is performance affected? 

I hadn't realized when I wrote that that the ComputedAttribute 
would have the attribute's name. That does make getattr faster.

But:
 
> Actually, I was justifying Python's current behavior as much as I
> was the current behavior. A truly general __getattr__ would
> access all attribute tests, not just non-existent attributes and
> thus be symmetric with setattr. But that would also be slow as
> hell.

I use __getattr__ most often to do lazy evaluation. So:

def __getattr__(self, nm):
   if nm == 'doohickies':
      rslt = self.doohickies = <long calc>
      return rslt

Yes, you can do this either way, but I rather like the fact that 
it's a last resort hook.
 
> > class A:
> >   def __get_y__(self):
> >     ...
> > class B(A):
> >   def __set_y__(self, value):
> >     ....
> 
> My first instinct is just to disallow this and figure it out
> after we have some more usage information/implementation
> resources. We could just say that all methods must be defined in
> the same class and if not. In other words, the methods "shadow
> each other" as a group.
> 
> This is no more messy than inherited __getattr__ s.

Not at all. The complexity of doing __getattr__ and __setattr__ 
hooks is all in the class you implement them in. There's no 
*new* complexity in going to base class implementations - 
you just chain as usual. The only surprise might be that if the 
hapless subclasser doesn't realize that some base class has 
a __setattr__ hook set.

I'm not against this proposal. But I think there are *no* "safe" 
ways of doing attr hacks; and simply getting to "safer" may be 
a whole lot of work.

[I also note that there are quite a few Python developers who 
want their users to say "x = obj.getx()", but want Python to 
create the accessor method "getx(self)" (if they haven't written 
one) and outlaw "x = obj.x". I have no sympathy for them, but 
they exist.]


- Gordon