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

Walter Dörwald walter at livinglogic.de
Fri Apr 7 20:16:42 CEST 2006


Guido van Rossum wrote:

> On 4/5/06, Walter Dörwald <walter at livinglogic.de> wrote:
>> 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.
> 
> Using @overloaded functions I would create an explicit class variable
> which is the @overloaded adapter rather than trying to make the
> interface also *be* the adapter. I would define the Interface class
> like this:
> 
> class InterfaceMetaclass(type):
>     # I hate anonymous metaclasses
>     def __new__(mcl, name, bases, dict):
>         # Give each class it's own registry
>         dict["adapter"] = overloaded(None)  # set default_function to None
>         return type.__new__(mcl, name, bases, dict)
> 
> class Interface:
>     __metaclass__ = InterfaceMetaclass
>     # No need for adapt and register methods here
> 
> The registration would then look like this:
> 
> Sequence.adapter.register(list)(PythonSeqAsSequence)
> 
> and the invocation would look like this:
> 
> print Sequence.adapter([1,2,3]).len()

This looks reasonable enough, as the adapter comes for free and it
avoids the problem of a __call__() in the Interface or its metaclass.

But if I'm implementing an adapter for a certain type, I still can't
make the returned object an instance of Interface (or I could, but it
would inherit an adapter, which is useless), so I would have:

class Interface:
   def getitem(self, index): ...
   def len(self, index): ...

Interface.adapter() which I have to call to do the adaption and

class PythonSeqAsSequence:
   def getitem(self, index): ...
   def len(self, index): ...

which implements the Sequence protocol, but shouldn't subclass Sequence.

Somehow this feels awkward to me.

Servus,
   Walter


More information about the Python-3000 mailing list