Is there a way to 'mask out' inherited methods?

Alex Martelli aleax at aleax.it
Sat May 4 11:55:36 EDT 2002


Ralf Juengling wrote:

> Alex Martelli <aleax at aleax.it> writes:
> 
>> so you can now do that (slowly:-) where earlier you could not,
>> and you could try your hand at writing a custom metaclass for
>> such masking purposes (not trivial), but by far the best approach
>> remains as it always was to eschew inheritance when it does
>> not meet your needs in favour of automatic (possibly selective)
>> delegation/aggregation/containment.
> 
> What dou you mean with 'automatic' in your last sentence?

I mean automatic, as in a typical dictionary definition:
"Acting or done as if by machine; mechanical".

A simple example would be:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52295


> I don't like to write wrappers (trivial code, yes) to the
> aggregated object to expose a subset of its methods.
> Is there a way to avoid writing wrappers?

You don't need to wrap every method individually, of course,
but you do have to provide either a list of method names to
be forwarded/delegated, or one of names to be 'blocked'; your
__getattr__ will have to be coded accordingly.

Note that __getattr__ is only called when an attribute is NOT
found "by normal means" -- this is what makes it reasonably
fast.  For a newstyle class, you can also choose to code
__getattribute__ instead -- now THAT is called for EVERY
access to an attribute, and it's fully responsible to determine
whether and how said attribute is "found" (most often it
delegates up to object.__getattribute__(self, name) except
for some names that it wants to deal with differently).  I
think I already showed you an example of __getattribute__,
but, alas, it IS slow, because it keeps getting called for
EVERY attribute access on your object.

In particular, __getattr__ is not called for methods that you
inherit -- because those are found "by normal means".

Here's an example of an object that wants to "be" a list in
some sense -- but, only in the read sense; it doesn't want
to be altered by any means the list class might supply,
including __setitem__, __setslice__, __delitem__, append,
extend, pop, etc, etc.  Its alleged reason for being is to
support a fast __contains__ via an auxiliary dictionary.

class rolist(object):
    _tolist = '''__add__ __mul__ __eq__ __ge__ __getitem__
        __getslice__ __gt__ __le__ __len__ __lt__ __ne__
        __repr__ __rmul__ __str__ count index'''.split()
    _tolist = dict(zip(_tolist,_tolist))
    def __init__(self, *args):
        self.L = list(*args)
        self.D = dict(zip(self.L,self.L))
        self.H = None
    def __radd__(self, other):
        return other + self.L
    def __contains__(self, item):
        return item in self.D
    def __iter__(self):
        return iter(self.L)
    def __hash__(self):
        if self.H is None:
            self.H = hash(tuple(self.L))
        return self.H
    def __getattr__(self, name):
        if name in self._tolist:
            return getattr(self.L, name)
        raise AttributeError(name)

I may have forgotten something, and this is untested code,
but I hope it does give the right general idea.  The main
enemy of automatic delegation is the benighted code that
checks for an object's type (type(x)==type(y) the worst,
which not even inheriting can help with, but even slightly
less benighted code that uses isinstance will choke on the
check if a rolist instance 'is-a' list -- just as it would
choke on an instance of UserList, etc).  PEP 246 (acceptance
and later widespread use of it) would deal with the issue,
for those rare cases where some form of 'type=checking' (or
better, _adaptation_) is actually needed, but most often the
blight of type-checking comes from wilful disregard for the
benefits of Python's powerful signature-based polymorphism.

For THAT, only education about the huge costs of typechecking
may perhaps provide a long-term solution (although appropriate
use of automatic weapons is certainly a tempting alternative,
I suspect the Python community, being so friendly and welcoming
and all that, might frown on that, sigh -- go figure).


Alex




More information about the Python-list mailing list