[Python-Dev] *very* revised proposal for interfaces

John Williams jrw@pobox.com
Wed, 02 Oct 2002 02:10:22 -0500


After re-reading PEPs 245 (interface syntax) and 246 (object 
adaptation), I hit on a new idea that seems to do everything these two 
PEPs and my original proposal (plus revisions) do, all with much less 
fuss.  I also realized I was adding a lot of excess complexity in order 
to implement all the rigorous error checking that doesn't really make 
sense in the Python world anyway (thanks for nudging me in that 
direction!)  As a side effect it's also a lot easier to explain.

Here's the new version, in a no-frills stripped-down form, explained by 
example.


# Create an interface--not much new here, just simpler syntax and fewer 
frills.
class I(interface):
  prop1 = "Docstring for property prop1."
  # It would be good form to put an argument list here,
  # but this method will never be called so it's not necessary.
  def meth1(): "Docstring for method meth1."

# Create a class implementing the interfac.
# It inherits from "object" explicitly, since a class whose bases
# are all interfaces is itself an interface, not an implementation.
class C(object, I):
  def meth(self,a,b): print "This is I.meth1 implemented by C."
  # (I'm not quite sure how to handle properies yet.)

assert I not in C.__bases__ # Interfaces are not really base classes!

# Example method requiring an object supporting interface I.
def usingI(x,y):
  # Check that x supports interface I.  Return either x or a suitable
  # wrapper around x that supports I's methods.  If x doesn't support I,
  # raise an exception.
  x = I(x)
  # Check that y also supports I.  If it doesn't, return None instead of
  # raising an exception.
  y = I(y, None)
  ...

# Example of binding an existing class to an interface.  Like creating
# an ordinary implementation, the bases are an interface and a regular
# class, but instead of creating a usable new class, it just associates
# an interface with a class and defines how the interface's methods are
# implmented.  The magic class name __binding__ triggers this behavior.
# Instances of this wrapper class are created by calling interface I.
class __binding__(str, I):
  def meth1(self,a,b): # Say what meth1 does on strings.
     assert isinstance(self, str)
     print "I.meth1 called on", self

# Example of using __binding__ to resolve a conflict between two interfaces.
class J(interface):
  def meth1(): "No relation to I.meth1!"
class D(object, I, J): # Implement two interfaces.
  # Define *both* I.meth1 and J.meth1 for this class.
  def meth1(self,a,b): print "This is I.meth1 implemented by D."
class __binding__(D,J):
  def meth1(self): print "This is J.meth1 implemented by D."

# Now to call these two methods, we can do this:
d = D()
i = I(d)     # Note i could really just be d here, not that it matters.
i.meth1(1,2) # Prints "This is I.meth1 implemented by D."
j = J(d)     # Here j must be a wrapper to make things work right.
j.meth1()    # Prints "This is J.meth1 implemented by D."
# Or use different syntax to be more explicit.
I.meth1(d,1,2) # Same as I(d).meth1(1,2)
J.meth1(d)     # Same as J(d).meth1()


Please let me know if I'm just insane for sleep deprivation!

jw