Yet Another PEP: Query Protocol Interface or __query__

Carlos Alberto Reis Ribeiro cribeiro at mail.inet.com.br
Mon Mar 26 10:22:53 EST 2001


Clark,

The main point of my implementation was the idea of building the adapter 
from a empty 'Protocol' class, adding to it methods that are bound to the 
original object. This and a few helper functions solve the first part of 
the problem (the object returning an adapter).

Please also note that I made a clear division between an 'object instance' 
and a 'protocol instance'. The protocol instance, or adapter, is a proxy 
object. It's implemented as a normal Python object, but all its methods are 
redirected as described above. This approach has some advantages:

a) The proxy adapter is completely transparent to the object instance.
b) The adapter maps only the methods that are mandatory by the protocol 
specification. This leads to better encapsulation and abstraction (all 
details of the object are hidden from the protocol user).
c) Method names may be different in the object. The protocol does the name 
translation.
d) The protocol instance may point to methods in disparate objects, which 
leads to a interesting type of 'composition'.

Anyway, did you take a look at my example? Please disregard the explanation 
of the protocols in terms of interfaces. The details can be changed without 
changing the essence of the proposal.


Now some comments, in a different order from your original email; I moved 
the two-way adapter discussion to the end of the message.

At 23:08 25/03/01 -0500, Clark C. Evans wrote:
>On Mon, 26 Mar 2001, Carlos Ribeiro wrote:
> > 1) What is a protocol? The simplest definition that I cant think of is: a
> > protocol is just a bunch of methods. An object is said to support a
> > protocol if it implements all methods of the protocol specification. The
> > specification defines the names and parameters of all methods.
>
>This is what an interface is, and there is a proposal
>for this.  A protocol should be more broadly defined, no?

Hummm... I see your point. My intention is different - avoid mixing 
'protocols' and 'class definitions'. One of the reasons was the potential 
for name clashes, as shown in my example. A protocol does not need to be 
represented *exclusively* by a class in the hierarchy. A single class may 
implement multiple protocols, but some protocols may need a composition of 
objects to do the work. For example, a composite object may contain lots of 
objects that gets exposed through interfaces in the container.

Also I see no reason to avoid defining some kind of 'simple' interfaces in 
this PEP - in the end, protocols are better mapped to 'interfaces' than to 
'classes'. It's much more flexible in this way.

OTOH, it is possible to use 'protocol classes' instead of strings to 
describe the protocol. The implementation does not change very much. The 
catch here is to avoid the assumption that *implementing* a protocol is the 
same as inheriting from the abstract protocol class. For example:

class ProtocolSequence:
   """ Protocol to implement sequence-like objects """
   def __len__ (self): pass
   def __getitem__(self, key): pass
   def __setitem__(self, key, value): pass
   def __delitem__(self, key): pass

In this example, ProtocolSequence is the specification of the methods used 
for sequence type objects. Sequence objects does not need to be derived 
from ProtocolSequence; they may be written from the scratch, or be derived 
from UserList, etc. Again, keep in mind the potential of composition as a 
technique to build large systems. It is important to avoid the assumption 
that inheritance is *the only way* to build OO systems.

MakeProtocol would then use some getattr/setattr 'magic' to build the 
method list, and to 'rebind' all the methods to the ones in the object 
being adapted. I'm not checking the method signatures though; this would 
need some help from the compiler in order to work.

(note: MakeProtocol is not a good name. MakeAdapter, or 
MakeProtocolInstance, is better)

>IMHO, the goal was to have this dialogue...

The implementation that I sent March 25 is one-way only. It is somewhat 
simpler, because I dont need to define a separate protocol class - it's 
just a matter of building the adapter inside the __adapt__ method. Also, I 
did not see yours (Clark's) dialogue implemented in the last version of the 
draft (I have read the one from March 21, did I miss something)?

TWO-WAY IMPLEMENTATION

For a two-way implementation, I suggest the following dialogue (about the 
same as your):

   Hello protocol manager, do you know if this
   object X here supports the protocol Y?

   The protocol manager then does:

     Hello object X, do you support protocol Y?
     Please return an adapter if so.

     Object X says: yes, I do support Y, and here
     is the adapter. Otherwise, X would have nothing
     to give back to the protocol manager.

     if the object doesn't know about protocol Y,
     then the protocol itself is asked about X:

     Protocol Y, do you know about object X?

     Protocol Y says: Yes, I checked object X
     class/methods/protocols, and I think that I
     can make an adapter for you.

I initially had some concerns over the two-way implementation. The main 
reason is that only an object could possible know how to adapt itself to a 
given protocol. Now I see that this could be possible, specially if we 
provide some helper functions to hide some hacks that Y would need to do in 
order to check X structure. I see three things that could be checked by the 
protocol:

a) class inheritance, as in your example. However it does limit 'protocol' 
to 'root classes', and it does have a problem with multiple inheritance, 
namely because of potential name clashes. It also does not work if the 
object is built not by inheritance, but by *composition*.

b) methods, or 'interface signature'. In this case the protocol just checks 
if the object does support all methods required by the protocol.

c) protocol compatibility. The protocol may ask the object directly for 
some compatible protocol, and then built a new adapter. This could be done, 
for instance, if you have a newer version of a protocol with only small 
changes; the protocol itself provides the interoperability layer with the 
previous version.


Some helper functions could be provided to help in these cases, for example:

def TestMethods(object, list_of_method_names):
   """ check to see if object supports all methods
       listed on the second parameter
   """
   (...)

I'll try to build a new example with these points in mind.


Carlos Ribeiro






More information about the Python-list mailing list