[Python-3000] iostack and Oh Oh

Steven Bethard steven.bethard at gmail.com
Sat Dec 9 18:11:32 CET 2006


On 12/9/06, Josiah Carlson <jcarlson at uci.edu> wrote:
>
> I'd prefer to see...
>
>     class MyMapping:
>         @implements(mapping)
>         def __getitem__(self, key):
>             ...
>
>         @implements(mapping)
>         def __len__(self):
>             ...
>
[snip]
>
> P.S. Here's a 10 minute implementation of the above semantics that is
> *almost* backwards compatible with Python 2.3 (except for the decorator
> thing, which Python 2.3 has to do without).
>
>
> _type = type
>
> def make_interface(name, methods):
>     return _type(name, (object,),
>              dict([(nam, object()) for nam in methods]))
>
> mapping = make_interface('mapping', mapping_methods)
> sequence = make_interface('sequence', sequence_methods)
> #...
>
> _supports = {}
>
> def supports(cls, interface):
>     if interface in _supports:
>         return cls in _supports[interface]
>     return False
>
> def implements(*interfaces):
>     def foo(fcn):
>         return implements_wrapper(fcn, interfaces)
>     return foo
>
> class implements_wrapper(object):
>     __slots__ = ['fcn', 'interfaces']
>     def __init__(self, fcn, interfaces):
>         self.fcn = fcn
>         self.interfaces = interfaces
>     def __call__(self):
>         return self.fcn, self.interfaces
>
> def type(*args):
>     if len(args) != 3:
>         return _type(*args)
>     name, bases, dct = args
>     dct2 = {}
>     for nam, obj in dct.items():
>         if isinstance(obj, implements_wrapper):
>             f, i = obj()
>             dct2[nam] = i
>             dct[nam] = f
>     cls = _type(name, bases, dct)
>     for nam, i in dct2.iteritems():
>         for j in i:
>             #default dict that produced sets would work well here
>             _supports.setdefault(getattr(i, nam), {})[cls] = None
>     return cls
>
> __builtins__.type = type


Sorry, I don't understand this implementation.  I just tried it and
the following fails because ``__getitem__`` is now an
``implements_wrapper`` that accepts the wrong arguments::

    class C(object):
        @implements(mapping)
        def __getitem__(self, item):
            return 42

    C()[1]

But I think the ``implements()`` decorator is a good way to approach
this.  Here's a different approach to it::

    def supports(cls, func_or_funcs):
        try:
            iter(func_or_funcs)
        except TypeError:
            return func_or_funcs in cls.__supports__
        else:
            return cls.__supports__.issuperset(func_or_funcs)

    def implements(*interfaces):
        def decorate(func):
            func.__interfaces__ = interfaces
            return func
        return decorate

    class interface_monitor(type):
        def __init__(cls, name, bases, bodydict):
            cls.__supports__ = set()
            for name, value in cls.__dict__.items():
                try:
                    interfaces = value.__interfaces__
                except AttributeError:
                    pass
                else:
                    for interface in interfaces:
                        method = getattr(interface, name)
                        cls.__supports__.add(method)

Basically, each function is identified with a set of interface
namespaces, e.g. ``mapping``, and the metaclass then uses
``getattr()`` to get the specific functions, e.g.
``mapping.__getitem__``, and add them to the set of operations the
class supports, i.e. ``cls.__supports__``.  Here's how you might
actually use this code::

    class Mapping(object):
        def __iter__(self):
            pass
        def __getitem__(self, key):
            pass
        def get(self, key, default=None):
            pass

    minimal_mapping = Mapping.__iter__, Mapping.__getitem__
    complete_mapping = minimal_mapping + (Mapping.get,)

    class C(object):
        __metaclass__ = interface_monitor

        @implements(Mapping)
        def __getitem__(self, key):
            return 4

        @implements(Mapping)
        def __iter__(self):
            return [4, 4, 4]

    print supports(C, Mapping.__iter__)     # prints True
    print supports(C, Mapping.__getitem__)  # prints True
    print supports(C, minimal_mapping)      # prints True
    print supports(C, complete_mapping)     # prints False

Note that with this code, an interface is just a set of methods, so
you can use ``supports()`` equally well to check for support of
individual methods or to check support for whole sets of methods.

STeVe
-- 
I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a
tiny blip on the distant coast of sanity.
        --- Bucky Katt, Get Fuzzy


More information about the Python-3000 mailing list