[Python-3000] Adaptation vs. Generic Functions

Tim Hochberg tim.hochberg at ieee.org
Thu Apr 6 18:15:54 CEST 2006


Walter Dörwald wrote:
> Guido van Rossum wrote:
> 
> 
>>On 4/5/06, Walter Dörwald <walter at livinglogic.de> wrote:
>>
>>>The problem with this is that it looks in the base protocol only if the
>>>the class can't be found in the registry of the subprotocol. This mean
>>>that an adapter from the subprotocol might be used, although the base
>>>protocol has a "better" adapter (i.e. one whose class that is nearer to
>>>real class of the object.
>>
>>It also doesn't look like it will work right for recursive invocations
>>of the adapter by some implementation (as happens to be common for the
>>pprint example).
> 
> 
> If the protocol was a class instead of an instance we could use the mro 
> of the protocol:
> 
> class MetaInterface(type):
>      def __new__(mcl, name, bases, dict):
[...]
> 
> class Protocol(object):
>       __metaclass__ = MetaInterface
> 
> # Extensible repr protocol
> class xrepr(Protocol):
>      @classmethod
>      def default(cls, *args, **kwargs):
>          return repr(*args, **kwargs)
> 
> @xrepr.register(list)
> def xrepr_list(protocol, obj):
>      return "[%s]" % ", ".join(protocol(x) for x in obj)
> 
[...]


Here's another approach to this that doesn't use metaclasses (they make 
my head hurt).  First we define a ChainedDict class that forwards 
requests for items it doesn't have onto it's parent:


_missing = object()

class ChainedDict(dict):
     """A dict like object that forwards item requests to its parent if 
neeeded."""
     def __init__(self, parent):
         dict.__init__(self)
         self.parent = parent
     def __contains__(self, key):
         return dict.__contains__(self, key) or (key in self.parent)
     def __getitem__(self, key):
         x = self.get(key, _missing)
         if x is _missing:
             raise KeyError('not found')
         return x
     def get(self, key, default=None):
         x = dict.get(self, key, _missing)
         if x is _missing:
             return self.parent.get(key, default)
         return x


[I believe that this satisfies the properties that Walter wants, but 
I've already been wrong on this once already so we'll see.]

With that in place, we make a minor change to the constructor of Protocol:


class Protocol(object):
     """Declare a named protocol"""
     def __init__(self, parent=None):
         if parent is None:
             self.registry = {}
         else:
             self.registry = ChainedDict(parent.registry)
    #....


Now "subclassing" a protocol is acomplished by passing it a parent.


subprotocol = Protocol(parentprotocol)


This is what I'm using in pprint and so far it seems to be working well. 
I think that this may be relevant to the stuff that Nick just posted 
about sane transitive adaption. I haven't fully digested that yet, but 
it seems that it would be simple enough to extend ChainedDict and 
Protocol to have multiple parents. Then combining two Protocols could be 
achieved by:


comboprotocol = Protocol(protocol_1, protocol_2)


I'll think about this more and post in the other thread if anything 
comes of it.

You'll note that I've ditched the name parameter in the Protocol 
constructor. It seemed nice to be able to have some minimal information 
about the protocol available by introspection, but in practice it seemed 
more and more like cruft. I think the correct spelling of a protocol 
with a specific name may be:

class MyProtocol(Protocol): pass
myprotocol = MyProtocol()
print myprotocol => <mymodule.MyProtocol object at 0x00B7E450>

regards,

-tim



More information about the Python-3000 mailing list