[Python-3000] iostack and Oh Oh

Tim Hochberg tim.hochberg at ieee.org
Thu Dec 7 21:18:05 CET 2006


Talin wrote:
> Phillip J. Eby wrote:
>> At 09:59 AM 12/5/2006 -0600, Guido van Rossum wrote:
>>> My point is that an interface can *document* (at least in English) a
>>> "contract" about the invariants between operations. While I'm not into
>>> enforcing or verifying such contracts, I'm very interested in
>>> documenting them. For example, something that has "mapping" behavior
>>> has a very different relationship between x[y] and "y in x" than
>>> something that has "sequence" behavior.
>> I assumed this didn't need answering.  If you're using the interface solely 
>> for documentation, then a namespace-oriented interface suffices to provide it.
> 
> I'm guessing that Guido's use of the word 'document' means something 
> more than just conveying information to a human reader.
> 
>  From what I can tell, the argument boils down to this: You are saying 
> that a class is merely the sum of its attributes and methods, and Guido 
> is saying that it's not.

That's not my interpretation. Let me throw out my spin on my 
understanding of Philip's position.

Here goes: to the extent that the semantics aren't clear from the 
methods it is sufficient, and preferable, to attach the semantic 
information to the methods rather their associated class.

> For example: You cannot deduce by examining the methods of a list that 
> the keys are required to be consecutive integers starting from 0. Lists 
> have __getitem__, but so do maps; They support len(), but so do maps; 
> and so on.

No, but if you tag the methods of the class then it appears that this works.

> 
> Thus, lists embody a particular behavior contract (called 'concept' in 
> C++ template land). (The word 'interface' and 'ability' has been used in 
> this context, but that's somewhat misleading. [1] [2]) The concept is 
> supported by the methods of the class, but is only incompletely 
> derivable from them.
> 
> I think what Guido is looking for is a way to signal that a class obeys 
> a given behavioral contract, and a way to inspect the object, both at 
> runtime *and* to a human reader, and discover what contracts are 
> supported by the class.

Let me throw out an examples of how this could look.

   @implements(sequence.__getitem__, sequence.__len__)
   class MySequence:
       #....

   @implements(mapping.__getitem__, mapping.keys, mapping.__len__)
   class MyMapping:
       #....

   #...

   # supports reads better than has_ability to me in this context.
   if supports(someobject, mapping.__getitem__):
      # do something
   else:
      # do something else


Assuming that class decorators make it in, I believe that this could be 
implemented in terms of generic functions or a simple registry with no 
additional syntactic support.

On the plus side, this operates at the same level of granularity as duck 
typing and would fit more naturally into the existing duck typing scheme 
IMO. Also, it avoids having to muck with bases when retrofitting classes 
to support a [concept/interface/ability], which gives me the heebie jeebies.

Let's move on to Guido's set example:

[Guido writes]
> Now I'd like to introduce a standard set implementation. It implements
> all those operations. But I'd like it to automatically accept operands
> of other set implementations, as long as they claim the relevant
> ability. For example, in Python 2.4, the built-in set implementation's
> __and__ is defined roughly like this (ignoring some optimizations,
> like that it's written in C :-):
> 
> # This a method of class 'set', a concrete set implementation
> 
> def __and__(self, other):
>   if not isinstance(other, baseset):
>     return NotImplemented
>   result = set()
>   for elem in self:
>     if elem in other:
>       result.add(elem)
>   return result

This could be written:

   def __and__(self, other):
     if not supports(other, set.__contains__):
         returns NotImplemented
     result = set()
     for elem in self:
       if elem in other:
         result.add(elem)
     return result

Where the other class has been marked as implementing set.__contains__. 
For example:

   @implements(set.__contains__)
   class MySet:
     #....


> But I'd like to be able to write it like this instead (still inside
> the 'set' class):
> 
> def __and__(self, other: MinimalSet):
>   result = set()
>   for elem in self:
>     if elem in other:
>       result.add(elem)
>   return result

There are several ways one could translate the above. If we let a tuple 
of these markers denote the union of all those types, one way would be 
to simply define:

   MinimalSet = (set.__contains__,)

And then the above definition could remain unchanged. Similarly:

   IterableSet = (set.__contains__,set.__iter__)
   StandardSet = (set.__contains__,set.__iter__,set.__and__,...)

This gives you the ability to express the three main types of sets, but 
I could also indicate, for example, that I have an IterableSet that also 
supports '&' using (set.__contains__,set.__iter__,set.__and__). In my 
experience this type of partial implementation is pretty common: since I 
don't always need all of the pieces, I just implement those parts that I 
do. If I can only spell three types of set, I'm forced to do extra work 
to implement the other methods or lie about what the class supports, 
neither of which is ideal.




-tim



More information about the Python-3000 mailing list