Bypassing __getattribute__ for attribute access

Bruno Desthuilliers bdesth.quelquechose at free.quelquepart.fr
Thu Oct 25 17:13:19 EDT 2007


Adam Donahue a écrit :
> Bruno,
> 
> I appreciate your attempt to answer my questions below, although I
> think my main point was lost amongst all your commentary and
> assumptions.  :^)    

Possibly. I sometimes tend to get a bit verbose !-)

>  I'm not inexperienced,

Obviously not.

> but I take the blame for
> the rambling initial post, though, which probably lead to the
> confusion.
> 
> So let me be more direct:
 >
>>From reading it seems that, indeed, __getattribute__ handles calling
> attribute.__get__( obj, type(obj ), which does the binding of the
> attribute to the object.

Yes.

> That is:
> 
> <thingy>.<attribute>
> 
> in turn is a call to
> 
> <thingy>.__getattribute__( '<attribute>' )    <1>

Yes.

> The default body of __getattribute__ in turn fetches the associated
> value (by traversing the class hierarchy if necessary) and examines
> its type in order to determine what to return.

Yes.

> Let's assume the ultimate value associated with the attribute (key) is
> v.
> 
> If type(v) is a function,

If v supports the descriptor protocol (which is the case for function 
objects and property objects) AND v is a class attribute

> __getattribute__ returns
> v.__get__( <thingy>, type( <thingy> )

Yes.

> If type(v) is, say, an integer, 

If v doesn't support the descriptor protocol

> __getattribute__ returns v unmolested.

Yes.

> And so forth.
> 
> So:
> 
>>>>class X( object ):
> 
> ...     a = 1

You understand that 'a' is a class attribute, don't you ?

> ...     class b( object ): pass
> ...     def c( self ): pass
> ...
> 
>>>>X.a
> 
> 1
> 
>>>>X.b
> 
> <class '__main__.b'>
> 
>>>>X.c
> 
> <unbound method X.c>
> 
>>>>x = X()
>>>>x.a
> 
> 1

Returns type(x).a

>>>>x.b
> 
> <class '__main__.b'>

idem

>>>>x.c
> 
> <bound method X.c of <__main__.X object at 0x81b2b4c>>

Yes.

> If my interpretation is correct, the X.c's __getattribute__ call knows
> the attribute reference is via a class,

Yes

> and thus returns an unbound
> method 

It returns c.__get__(None, X). Which is implemented to return an unbound 
method if the first arg is None.

> (though it does convert the value to a method).

__getattribute__ doesn't convert anything here - and FWIW, doesn't care 
if c is a function or property or whatnot. The only thing it looks for 
is if c has a __get__ method.

>  Likewise,
> x.c's __getattribute__ returns the value as a method bound to the x
> instance.

Yes, because then __getattribute__ returns x.c.__get__(x, X), which, 
since c is a function, returns a bound method.

> How does __getattribute__ knows the calling context.  Its first
> argument is the attribute name from what I can tell, not the object
> calling it.

Really ?-)

__getattribute__ is itself a function and an attribute of a class (and 
FWIW, it's signature is __getattribute__(self, name)). So when itself 
looked up, it returns a method object, which when called passes the 
'target' object as first arg.

You already know that Python's classes are objects (instances of type 
'type'). So you should by now understand how __getattribute__ knows it's 
target. Got it ? Yes, right: when looking up attributes on a class, it's 
the class's class (IOW: the type)  __getattribute__ method that is 
invoked !-)

> Is this correct so far?

cf above.

> Moving on to __get__.  Assume:
> 
> class X( object ):
>     def foo(self):
>         print `self`
> x = X()
> 
> Then:
> 
> x.foo()
> 
> Is similar (perhaps the same as) to:
> 
> X.foo.__get__( x, X )()

Almost but not quite. It's:

x.foo() <=> X.foo(x) <=> X.foo.im_func.__get__(x, X)()

It's a bit less confusing when the function is defined outside the class:

def bar(self):
   print self

X.bar = bar

x = X()

Now you have:

x.bar() <=> X.bar(x) <=> bar.__get__(x, X)()


> (__getattribute__ performs the transformation automatically when one
> references via the . operator.)
> 
> And so one can do: 
> 
>>>>class X( object ):
> 
> ...     x = 1

Here, x is a class attribute.

> 
>>>>def set_x( self, x ): self.x = x

Now this creates an instance attribute that will shadow the class attribute.

> ...
> 
>>>>x = X()
>>>>set_x.__get__( x, X )( 5 )
>>>>x.x
> 
> 5

So far, so good.

> The logical next question then is how does one best add a new method
> to this class so that future references to x.set_x() and X.set_x will
> properly resolve?  It seems the answer would be to somehow add to
> X.__dict__ a new value, with key 'set_x', value the function set_x.

Yes. Which is very simply done by just binding set_x to X.set_x. Just 
like I did above. Dynamically adding methods to classes is pretty 
straightforward, the tricky point is to dynamically add methods to 
instances, since the descriptor protocol is only triggered for class 
attributes. But you obviously found how to do it using func.__get__(obj, 
type(obj)) !-)

>>From there on the . operator I assume would perform the binding to X
> or x as needed on-the-fly.

Yes.

NB: please some guru around correct me if I said something wrong (Alex ? 
Tim ? Fredrick ? If you hear me ?)



More information about the Python-list mailing list