Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

How does "delegation" work as per the FAQ?

0 views
Skip to first unread message

Rene Pijlman

unread,
Dec 25, 2003, 12:08:24 PM12/25/03
to
Section 6.5 "What is delegation?" of the FAQ says:

"Python programmers can easily implement delegation. For example, the
following class implements a class that behaves like a file but converts
all written data to uppercase:

class UpperOut:
def __init__(self, outfile):
self.__outfile = outfile
def write(self, s):
self.__outfile.write(s.upper())
def __getattr__(self, name):
return getattr(self.__outfile, name)

[...] All other methods are delegated to the underlying self.__outfile
object. The delegation is accomplished via the __getattr__ method; consult
the language reference for more information about controlling attribute
access."
http://www.python.org/doc/faq/programming.html#what-is-delegation

I don't understand how __getattr__ accomplishes delegation of other
methods. I'd expect __getattr__ to be called for attribute access, not for
method calls. And how would the arguments of a method call be passed on?

The link to the language reference doesn't help much. It says about
__getattr__: "Called when an attribute lookup has not found the attribute
in the usual places". There is no mention of method calls.

--
René Pijlman

Ville Vainio

unread,
Dec 25, 2003, 1:24:56 PM12/25/03
to
Rene Pijlman <reply.in.th...@my.address.is.invalid> writes:


> __getattr__: "Called when an attribute lookup has not found the attribute
> in the usual places". There is no mention of method calls.

A method is an attribute.

--
Ville Vainio http://www.students.tut.fi/~vainio24

Rene Pijlman

unread,
Dec 25, 2003, 3:06:38 PM12/25/03
to
Ville Vainio:
>Rene Pijlman:

>> __getattr__: "Called when an attribute lookup has not found the attribute
>> in the usual places". There is no mention of method calls.
>
>A method is an attribute.

I see. But how are the arguments of a method call passed on, when
delegation is implemented this way?

--
René Pijlman

Jeff Epler

unread,
Dec 25, 2003, 3:20:57 PM12/25/03
to Rene Pijlman, pytho...@python.org
In Python, there's little difference between
f = o.m
f(3)
and
o.m(3)
either way, the attribute 'm' is looked up on o in exactly the same
way. So the delegation pattern works no matter whether 'o.m' is a
method or is data.

Jeff

Mike C. Fletcher

unread,
Dec 25, 2003, 4:29:56 PM12/25/03
to pytho...@python.org
Rene Pijlman wrote:

Jeff already gave you a fairly brief description of why this is so, but
I'll try to flesh it out somewhat. In Python, methods of objects are
actually themselves objects which wrap up an implementation function
(also an object) and a reference to the object on which they are to act
(the "self" parameter). To demonstrate this:

>>> class x(object):
... def y( self, value ):
... print value
...
>>> item = x()
>>> item
<__main__.x object at 0x011EA4B0>
>>> item.y
<bound method x.y of <__main__.x object at 0x011EA4B0>>
>>> x.y
<unbound method x.y>
>>> item.y.im_self
<__main__.x object at 0x011EA4B0>
>>> item.y.im_func
<function y at 0x011E4108>
>>> item.y.__call__
<method-wrapper object at 0x00ED3B78>

So, when you do item.y( somevalue ), you are doing this set of actions:

* doing an attribute lookup in the object item for the attribute name y
o in this example, this finds the unbound method x.y in the
class, which (via various hooks (see discussions of
"descriptors")) generates a new bound method object (item.y)
for the instance "item" and returns that.
o in the sample code you found, the methods for the file
object would not be found in the instance "item"'s
dictionary or class' dictionary, so the __getattr__ hook
(which is called when an attribute lookup fails) would be
called and accomplish the delegation by doing another
attribute lookup from the object to which it is delegating
responsibility.
* calling the __call__ method of the (newly created) bound method
object with the arguments (somevalue)
o in this example, the bound method has a reference to "item"
which it will pass as the first argument to it's underlying
"y" function, so the arguments passed to the function are
(item, somevalue) (corresponding to self, somevalue in the
method signature).
o In the sample code, the bound method would have a reference
to the delegated file, rather than the delegating object, so
it will act on the underlying file.

Enjoy yourself,
Mike

_______________________________________
Mike C. Fletcher
Designer, VR Plumber, Coder
http://members.rogers.com/mcfletch/


Bill Trenker

unread,
Dec 25, 2003, 9:40:53 AM12/25/03
to pytho...@python.org
Rene Pijlman wrote:

> I don't understand how __getattr__ accomplishes delegation of other
> methods. I'd expect __getattr__ to be called for attribute access, not for
> method calls.

In Python a method is a callable attribute. Since a method is also an attribute, __getattr__ works equally well with instance method attributes as with instance data attributes.

This can be very confusing for programmers coming from a statically-typed background. Try to keep in mind that Python is dynamically-typed. So in the case of attributes and methods, python doesn't care what an attribute represents until the last moment, at run-time, when the program attempts to call the attribute. It is only then that Python checks to see if the attribute is callable and treats it as a method. This is real paradigm shifting material for some of us. But once you get enough examples under your belt and the gears suddenly shift, the flexibility and elegance of Python jumps out at you.

> And how would the arguments of a method call be passed on?

First a quick review, by example:

>>> class A:
... def m(self, msg): print msg
>>> a = A()
>>> a.m
<bound method A.m of <__main__.A instance at 0x4026a5ec>>
>>> a.m('hello world')
hello world

When python looks-up attribute m in object a it returns the object bound to the attribute. At that point, a.m could represent any object: an integer, a list, a callable, etc.. Python will only attempt to call a.m when, at run-time, it comes across the () as in a.m('hello world'). The point here is that the look-up of the method's name is a completely separate step from calling it. The lookup (done internally by python in the above example) is done with getattr which only returns an object, it doesn't call it. So getattr is not involved with things such as function arguments or return values.

Building on the above snippet:

>>> class B:
... a = A()
... def __getattr__(self,name):
... return getattr(self.a,name)
...
>>> b=B()
>>> b.m('hello universe')
hello universe

The statement b=B() creates an instance of class B and binds the instance object to variable b. That instance will have an attribute a which is an instance object of class A.

The statement b.m('hello universe') first causes the attribute named m to be looked-up in object b. Class B doesn't define an attribute named m, but class B does have a __getattr__ attribute (method) so python calls it. The __getattr__ method uses the getattr function to lookup what boils down to attribute m in b.a. Since b's attribute a is an instance of class A which does have an attribute named m, the getattr is satisfied. The net effect is that the expression b.m resolves to the method object b.a.m.

So after all that, back in statement b.m('hello universe'), python sees the ('hello universe') after the b.m, verifies that the result of looking-up b.m is a callable object, and calls it with the enclosed argument. The reason the __getattr__ method's code doesn't need to deal with argument lists is that the call never happens there. In the example, it happens on the command line at statement b.m('hello universe'). In that statement, the __getattr__ method's code only involvement is with determining the object for the expression b.m.

I don't know if this attempted explanation helps or just adds to the confusion. I know how obvious these concepts are once you've grasped them. But I also know how frustrating it can be until you reach that sudden "aha! moment" and you realize you've got it.

Regards,
Bill

Terry Reedy

unread,
Dec 26, 2003, 12:25:23 AM12/26/03
to

"Rene Pijlman" <reply.in.th...@my.address.is.invalid> wrote in
message news:10gmuvc8pqt5opt7o...@4ax.com...

The delegation is done before the call, in order to find the (delegated-to
method) object on which to make the call. The alternative is to write a
wrapper method for each method delegated to and explicitly pass the args on
with *args and ** args. But doing the delegation before the call in the
method lookup make this not necessary and is much easier.

Terry J. Reedy


Rene Pijlman

unread,
Dec 26, 2003, 7:59:20 AM12/26/03
to
Terry Reedy:

>The delegation is done before the call, in order to find the (delegated-to
>method) object on which to make the call.

Ah, this is the missing link between my brain cells :-)

Thank you all for your kind explanation.

--
René Pijlman

0 new messages