[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