[Python-3000] Adaption & generic functions [was Generic functions]

Walter Dörwald walter at livinglogic.de
Wed Apr 5 09:43:04 CEST 2006


Guido van Rossum wrote:

> On 4/4/06, Tim Hochberg <tim.hochberg at cox.net> wrote:
> [...] 
>>     def __init__(self, name, doc=''):
>>         self.name = name
>>         self.registry = {}
>>         self.__doc__ = doc
>>         self.all_protocols.add(self)
>>     def __repr__(self):
>>         return "<protocol %r>" % self.name
>>     __str__ = __repr__
>>     def __call__(self, *args):
>>         for key in self.keysof(*args):
>>             adapter = self.registry.get(key, None)
>>             if adapter is not None:
>>                 return adapter(*args)
>>         raise ValueError('adapter not found')
> 
> So __call__ is what used to be call adapt. Or, rather, where Alex used
> to write adapt(x, P) and where I write P.adapt(x), you just write P().
> Clever.

I've used that in my example too (see 
http://mail.python.org/pipermail/python-3000/2006-April/000308.html)

But might closes off one route we could take if we're adapting to 
something that must provide more than one function/method (i.e. an 
interface) and we'd like to have the interface and the adaption registry 
be identical. Suppose we're designing a new interface for sequence. A 
sequence must provide getitem() and len() methods. If the adapt(at)ion 
API only used register and adapt methods we could do something like this:


class Interface(object):
    class __metaclass__(type):
       def __new__(mcl, name, bases, dict):
          # Give each class it's own registry
          dict["registry"] = {}
          return type.__new__(mcl, name, bases, dict)
	
    @classmethod
    def register(cls, adapter, T):
       cls.registry[T] = adapter

    @classmethod
    def adapt(cls, obj):
       for base in type(obj).__mro__:
          try:
             return cls.registry[base](obj)
          except KeyError:
             pass
       raise TypeError("can't adapt %r to %r" % (obj, cls))


class Sequence(Interface):
    def getitem(self, index): pass
    def len(self): pass


class PythonSeqAsSequence(Sequence):
    def __init__(self, obj):
       self.obj = obj

    def getitem(self, index):
       return self.obj[i]

    def len(self):
       return len(self.obj)

Sequence.register(PythonSeqAsSequence, list)


print Sequence.adapt([1,2,3]).len()


But if adapting is done via __call__() we have a problem: Sequence 
already provides a __call__, the constructor. Of course if this worked

print Sequence([1,2,3]).len()

would look much better than

print Sequence.adapt([1,2,3]).len()

but I'm not sure if it's possible to work around the constructor/adapt 
clash.

Bye,
    Walter Dörwald


More information about the Python-3000 mailing list