Extending objects through methods' __call__

Tim Peters tim_one at email.msn.com
Sun May 16 21:42:45 EDT 1999


[Hrvoje Niksic]
> Something appears to be lacking in Python's class interface.  If I
> have a class X and its instance x, then I can customize what x() will
> do (via X.__call__), what reading and assigning to x.foo will do (via
> X.__getattr__ and X.__setattr__), what reading and assigning to x[foo]
> will do (via X.__getitem__ and X.__setitem__) and a bunch of other
> things, but...  I cannot tell what x.foo() will do, at least not
> without knowing all the foo's in advance.  Or can I?

I'm not sure you're asking the question there you want to ask, but, if you
are, part of the *point* of OO is that you can't tell what x.foo() will do
via static inspection <wink>.

> I think it would be very nice if I were able to specify something
> like:
>
> class my_class:
>     def somemethod():
>         ...
>     def someothermethod():
>         ...
>     def __methcall__(self, method, *args, **kwargs):
>         # Handle all the method calls that are not named otherwise.
>         ...

Closest you get today is that __getattr__ handles *all* attribute references
that aren't satisfied otherwise.  As Mark said, it can't tell the difference
between data and method attr accesses; it's all or nothing.

> The practical implication of this would be that it would become
> trivial to write a wrapper around an intrinsic object.  UserDict could
> be written like this:
>
> class UserDict:
>     def __init__(self): self.data = {}
>     def __methcall__(self, method, *args, **kwargs):
>       return apply(self.data__methods__[method], args, kwargs)
>
> Would such a scheme work?

Alas no, but for a different reason:  objects of builtin types don't use the
same dispatching code as class instances; this is the infamous "type/class
split" the healing of which is the sometimes-charter of the Types-SIG; for
starters,

>>> {}.__methods__
['clear', 'copy', 'get', 'has_key', 'items', 'keys', 'update', 'values']
>>>

That is, the builtin dicts don't have __getitem__ etc methods, so trying to
redirect a __getitem__ to one directly will simply blow up (with an
AttributeError).  That's why UserDict & friends are needed now.

getattr-based delegation *does* work for Python classes, though:

class A:
    def __init__(self, data): self.data = data
    def x(self): return "x"
    def y(self): return "y"
    def __str__(self): return "A(" + str(self.data) + ")"

class B:
    def __init__(self, data):
        self.delegate = A(data)
    def y(self): return "y'"
    def __getattr__(self, attr):
        # let the delegate handle everything else
        return getattr(self.delegate, attr)

b = B('stuff')
print b.x(), b.y(), b.data, str(b)

That prints

    x y' stuff A(stuff)

charades-ly y'rs  - tim







More information about the Python-list mailing list