[Python-3000] Adaptation

Tim Hochberg tim.hochberg at ieee.org
Sun Apr 2 20:52:16 CEST 2006


Just in case anyone wants to play with a slightly more fleshed out, but 
still simple minded (in a good way) approach to adaption, I've put up an 
implementation here:
	http://members.cox.net/~tim.hochberg/adaption2.py
It's adaption2, because I decided I didn't like my first try. No 
guarantees that it's not full of bugs, wrongheaded, etc, but it does 
give something concrete to look at.

It's quite similar to the Alex's super-simple proposal, with two main 
differences.

1. Protocols are indicated by Protocol objects, not strings. These 
objects just have a name, and no other features, so they aren't any more 
meaningful than strings, but by dint of being objects they can't 
collide. This also makes cancelling protocols a bit simpler.

2. The adaption is P->P, that is from one protocol to another protocol, 
rather than from T->P, that is from type to protocol. This makes things 
marginally more complicated since you need to associate types with 
protocols, but seems better for reasons I can't really articulate right now.

As Alex suggested protocols are inherited by walking the mro for objects 
that have one.

I'll include the module docstring below.

Regards,

-tim



"""adaption -- the worlds fourth most naive implementation of adaption

Based on some post one python-dev-3000 on April 1st, 2006. Alex 
Martelli's being
one of the main ones. Idiocies are all atributable to me (Tim Hochberg), 
however.

The strategy is that objects have associated with them a set of 
protocols. These
protocols are just tags that represent some interface or behaviour of the
object. These tags are associated with the object by registering them
using register_type.

As an example, let's define a sequence type that starts it's indexing at 1:

 >>> class BaseOneSeq(object):
...     def __init__(self, values):
...         self._values = values
...     def __getitem__(self, i):
...         return self._values[i-1]

We need a protocol to describe this class and we then need to register 
the class
so the that the adaption machinery knows that the class and protocol are
connected.

 >>> base1seq = Protocol("base1seq")
 >>> register_type(BaseOneSeq, base1seq)

Now we want an adapter to adapt this to a normal sequence.

 >>> class BaseOneSeqToSeq(object):
...     def __init__(self, target):
...         self._target = target
...     def __getitem__(self, i):
...         return self._target[i+1]

Finally, we need to register this adapter:

 >>> register_adapter(base1seq, protocols.sequence, BaseOneSeqToSeq)

Phew. All done, now we can try it out:

 >>> b1 = BaseOneSeq([2,3,5,7,11,13,17,19,23])
 >>> b1[1], b1[3], b1[5]
(2, 5, 11)

So far, so good. Now to adapt it to a normal, base-0 sequence:
 >>> seq = adapt(b1, protocols.sequence)
 >>> seq[0], seq[2], seq[4]
(2, 5, 11)

Well, that's cool, but it seemed like a lot of work.

Now let's try out a cool, if possibly useless feature. AntiProtocols. 
This time,
we repeat the same problem, but we derive from list to get __len__ and stuff
for free:

 >>> class BaseOneSeq2(list):
...     def __getitem__(self, i):
...         return list.__getitem__(self, i-1)
 >>> register_type(BaseOneSeq2, base1seq)

Notice that this time around is much easier -- we've already done a lot 
of the
work.

 >>> b2 = BaseOneSeq2([2,3,5,7,11,13,17,19,23])
 >>> b2[1], b2[3], b2[5]
(2, 5, 11)
 >>> len(b2)
9
 >>> seq2 = adapt(b2, protocols.sequence)
 >>> seq2[0], seq2[2], seq2[4]
(23, 3, 7)

Huh!

The problem here is that we're inheriting from list and list is a sequence.
If an object already satisfies a protocol, it gets returned from adapt 
unchanged.
To fix that we need to nullify the sequence protocol that BaseOneSeq2 
gets from
list. AntiProtocols to the rescue:

 >>> register_type(BaseOneSeq2, ~protocols.sequence)
 >>> seq3 = adapt(b2, protocols.sequence)
 >>> seq3[0], seq3[2], seq3[4]
(2, 5, 11)

That's all folks.

"""



More information about the Python-3000 mailing list