Yet Another PEP: Query Protocol Interface or __query__
Carlos Ribeiro
carribeiro at yahoo.com
Sun Mar 25 22:23:59 EST 2001
I'm thinking about a slightly different approach for the __adapt__ PEP.
However, I need some help to clarify my ideas - english is not my first
language, so please be patient :-) I also did my homework, and I have a
very nice that works fine for me.
0) Let's start from the scratch (I'm not sure if we're trying to solve the
same problem)
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.
2) A 'protocol instance' is the minimal object instance that completely
supports the protocol.
3) Now we're getting closer. My proposal is to have a method called
__adapt__ that returns a 'protocol instance'.
4) So __adapt__ must convert a 'object instance' into a 'protocol
instance'. If the object implements the standard names with the default
names, this is pretty straighforward - the object could be used as a
protocol instance.
5) The problem occurs when the object supports the protocol using methods
with different names. Assume that we have two different protocols asking
for methods with the same names, but with different semantics and/or
parameters. An object that supports both protocols will need some way to
map its internal methods to the ones defined by each protocol.
6) We can solve (5) using a 'proxy', or 'adapter'.
EXAMPLE
Assume that we have these protocols:
a) protocol 'IntegerDivision'
supports the __div__() method for integer division
b) protocol 'FloatDivision'
supports the __div__() method for floating point division
#----------------------------------------------------------------------
class Protocol:
""" empty class that can be used for all protocol adaptation """
pass
def MakeProtocolDefault(protocol_methods, adaptee):
""" Returns the default protocol adapter for an object
The default uses the same name for all methods in the protocol
and in the object instance that is being adapted
"""
p = Protocol()
for pm in protocol_methods:
om = getattr(adaptee, pm)
setattr(p, pm, om)
return p
def MakeProtocol(protocol_methods, object_methods):
""" Returns the a protocol adapter for an object, mapping
each protocol_method to a object_method
"""
p = Protocol()
for pm, om in zip(protocol_methods, object_methods):
setattr(p, pm, om)
return p
class Number:
""" supports both protocols: 'IntegerDivision' & 'FloatDivision' """
def __init__(self, p_value):
self.value = p_value
def div_int(self, b):
# integer division
return int(self.value)/int(b)
def div_float(self, b):
# floating point division
return float(self.value)/float(b)
def __adapt__(self, protocol):
if protocol == 'IntegerDivision':
return MakeProtocol(['__div__'], [self.div_int])
elif protocol == 'FloatDivision':
return MakeProtocol(['__div__'], [self.div_float])
else:
return None
#----------------------------------------------------------------------
>>> n = Number(2)
>>> in = n.__adapt__('IntegerDivision')
>>> fn = n.__adapt__('FloatDivision')
>>> in/3
0
>>> fn/3
0.66666666666666663
>>> n.value = 5
>>> fn/3
1.6666666666666667
>>>
#----------------------------------------------------------------------
Some notes:
a) Please keep this in mind: I tried to keep the implementation as simple
as possible. I'm not trying to emulate any specific language construct. My
intention is just to provide a simple 'protocol adapter' layer for Python.
b) I'm using strings to represent protocol names. I fell that this is a
very simple and extensible approach. For example, the string could be a URL
pointing to the canonical description of the interface, made using XML.
c) The MakeProtocol helper function matches all methods desired in the
protocol with the ones implemented in the object. The actual syntax may me
different (eg passing a single sequence containing the pairs, or passing a
dictionary), but this is the basic idea.
d) The first parameter of MakeProtocol can be thought of as the 'protocol
definition'. However I dont have any specific suggestion on this right now
- thats why I'm using a simple list.
e) The Protocol class is empty; this was done to avoid potential name
clashes and to ensure that *any* protocol can be adapted using it.
f) A very simple optimization would be to prepare all 'protocol adapters'
on __init__, returning them inside __adapt__. This can be done if you
expect a lot of __adapt__ calls.
p.s. I think that it is possible to devise a highly optimized
implementation. In fact, if all protocols are known at compile time, this
should make it easier to optimize method calls, but this need to be
explored more carefully. This does not need to be made right now - as Clark
pointed out, may be it's better to see if people actually uses this.
pp.ss. It was not my original intention, but we could use this mechanism,
as exposed in my example, as a way to implement the PEPs that propose
semantic changes on the numeric types.
Carlos Ribeiro
_________________________________________________________
Do You Yahoo!?
Get your free @yahoo.com address at http://mail.yahoo.com
More information about the Python-list
mailing list