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

Tim Hochberg tim.hochberg at cox.net
Fri Apr 7 20:43:48 CEST 2006


Walter Dörwald wrote:

>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.
>  
>
Perhaps this is a hint that protocol isn't the right term here. The 
Protocol class in its current implementation doesn't describe an object, 
it's really just a factory function for creating objects that satisfy 
some other interface. Trying to do both in one object looks like it's 
just going to cause trouble. Suppose that instead of calling these 
protocols, we just call them factories. Then it's clear that these 
objects don't describe anything, they just make stuff out of other 
stuff.  As it stands, Walter's question seems inevitable, but if these 
were called something else the idea would probably seem nonsensical. The 
answer to "but what if I I'd like to have the interface and the factory 
function be identical?", assuming it were ever asked, could almost 
certainly be "don't do that!".

Regards,

-tim







More information about the Python-3000 mailing list