classes (was Re: Same again please for OOP)

Alex Martelli aleaxit at yahoo.com
Tue Dec 26 09:49:15 CET 2000


"Moshe Zadka" <moshez at zadka.site.co.il> writes:

> > That would be handy, and I like 'Boost Python' approach to that --
> > naming conventions rather than complex correspondences (see
> > www.boost.org, but i guess C++-loathers would rather ignore it:-).
>
> In spite of your warning, I went to the site, but couldn't figure
> out the relevance. Can you summarize what is the Boost Python approach?

See: http://www.boost.org/libs/python/doc/special.html, right
at the end of this longish page.

Any BPL-wrapped C++ class is a metaclass that lets Python
classes inheriting from it define per-attribute accessors;

"""
BPL checks the special names

__getattr__<name>__
__setattr__<name>__
__delattr__<name>__
to provide functional access to the attribute <name>. This facility can
be used from C++ or entirely from Python. For example, the following
shows how we can implement a ``computed attribute'' in Python:

>>> class Range(AnyBPLExtensionClass):
...    def __init__(self, start, end):
...        self.start = start
...        self.end = end
...    def __getattr__length__(self):
...        return self.end - self.start
...
>>> x = Range(3, 9)
>>> x.length
6
"""

I'd rather have the accessor-name shorter (__get_length__, for
example), but the key thing is simple naming convention.

At the C++-level, only, BPL also provides finer-grained control
of this through functions def_getter and def_setter to get read
and write access respectively for a given attribute name, and the
simpler def_readonly (which provides read access AND blocks any
write-access) and def_read_write (which provides r/w access).

I don't think we need such fine grain from the Python side, at
least not in 80-90% of the cases.

If one DID want it, I think the Pythonic way to provide it would
be through special named attributes, e.g. a dictionary from
attribute-names to (getter, setter) pairs -- or two dictionaries,
one for getters and one for setters.  The latter (and more besides,
admittedly) is basically what Mark Hammond's excellent win32com
does to map between COM properties and Python attributes; in
that case, something of that ilk is surely needed since several
little key pieces of metadata must be associated with each prop,
not just one or two methods.

While Abrahams and Köthe's Boost Python Library (BPL) uses
the metaclass hook to provide this, Hammond doesn't -- he does
it via __getattr__/__setattr__ (actually, he generates Python
code from COM type-libraries on the fly, caching it for future
accesses, and that Python code in turn uses __getattr__ &c -- a
neat hack indeed, but that's another issue:-).


Maybe I should clarify the implicit-delegation issue too.
Sticking to getters for brevity, this could be it today:

class GetterDelegatorMixin:
    def __init__(self):
        self._deldic_={}

    def delegateTo(self, delegate, *names):
        for name in names:
            self._deldic_[name] = delegate

    def removeDelegation(self, *names):
        for name in names:
            del self._deldic_[name]

    def removeDelegate(self, delegate):
        for name, whosit in self._deldic_.items():
            if whosit == delegate:
                del self._deldic_[name]

    def __getattr__(self, name):
        delegate = self._deldic_.get(name,None)
        if delegate is None:
            raise AttributeError, name
        return getattr(delegate, name)


As long as the __init__ and __getattr__ of this mixin are
appropriately called, any inheriting instance can choose
to delegate certain named attributes to equally named
ones of other objects (actually, the getattr for their
lookup might usefully be anticipated to delegateTo time,
but doing it at the very last instant might allow for even
_further_ fluidity...:-).  All sorts of design decisions are
somewhat arbitrary (_deldic_ maps name->delegate, so
to find everything mapped to a given delegate, e.g. for
total removal of that delegate's responsibilities, we need
to loop over all of its items -- I think that's OK because,
in the use-cases I have in mind, the map is not all that
big for any given object anyway, but considering other
use-cases might lead to different architectures), but as a
proof-of-concept I hope it may serve.


Alex



_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com





More information about the Python-list mailing list