[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