[Python-3000] PEP 3133: Introducing Roles

Benji York benji at benjiyork.com
Thu May 17 15:15:28 CEST 2007


Guido van Rossum wrote:
> On 5/16/07, Benji York <benji at benjiyork.com> wrote:
>> Guido van Rossum wrote:
>>> On 5/14/07, Benji York <benji at benjiyork.com> wrote:
>>>> Collin Winter wrote:
>>>>> PEP: 3133
>>>>> Title: Introducing Roles
>>>> Everything included here is included in zope.interface.  See in-line
>>>> comments below for the analogs.
>>> Could you look at PEP 3119 and do a similar analysis?
>> Sure.

And here it is:

 > PEP: 3119
 > Title: Introducing Abstract Base Classes

I've placed my comments in-line and snipped chunks of the original PEP
where it seemed appropriate.

 > Version: $Revision$
 > Last-Modified: $Date$
 > Author: Guido van Rossum <guido at python.org>, Talin <talin at acm.org>
 > Status: Draft
 > Type: Standards Track
 > Content-Type: text/x-rst
 > Created: 18-Apr-2007
 > Post-History: 26-Apr-2007, 11-May-2007

[snip]

 > Rationale
 > =========
 >
 > In the domain of object-oriented programming, the usage patterns for
 > interacting with an object can be divided into two basic categories,
 > which are 'invocation' and 'inspection'.
 >
 > Invocation means interacting with an object by invoking its methods.
 > Usually this is combined with polymorphism, so that invoking a given
 > method may run different code depending on the type of an object.
 >
 > Inspection means the ability for external code (outside of the
 > object's methods) to examine the type or properties of that object,
 > and make decisions on how to treat that object based on that
 > information.
 >
 > Both usage patterns serve the same general end, which is to be able to
 > support the processing of diverse and potentially novel objects in a
 > uniform way, but at the same time allowing processing decisions to be
 > customized for each different type of object.
 >
 > In classical OOP theory, invocation is the preferred usage pattern,
 > and inspection is actively discouraged, being considered a relic of an
 > earlier, procedural programming style.  However, in practice this view
 > is simply too dogmatic and inflexible, and leads to a kind of design
 > rigidity that is very much at odds with the dynamic nature of a
 > language like Python.

I disagree with the last sentance in the above paragraph.  While
zope.interface has been shown (in a seperate message) to perform the
same tasks as the "rolls" PEP (3133) and below I show the similarities
between this PEP (ABCs) and zope.interface, I want to point out that
users of zope.interface don't actually use it in these ways.

So, what /do/ people use zope.interface for?  There are two primary
uses: making contracts explicit and adaptation.  If more detail is
desired about these uses; I'll be glad to share.

My main point is that the time machine worked; people have had the moral
equivalent of ABCs and Roles for years and have decided against using
them the way the PEPs envision.  Of course if people still think ABCs
are keen, then a stand-alone package can be created and we can see if
there is uptake, if so; it can be added to the standard library later.

If I recall correctly, the original motivation for ABCs was that some
times people want to "sniff" an object and see what it is, almost always
to dispatch appropriately.  That use case of "dispatch in the small",
would seem to me to be much better addressed by generic functions.  If
those generic functions want something in addition to classes to
dispatch on, then interfaces can be used too.

If GF aren't desirable for that use case, then basefile, basesequence,
and basemapping can be added to Python and cover 90% of what people
need.  I think the Java Collections system has shown that it's not
neccesary to provide all interfaces for all people.  If you can only
provide a subset of an interface, make unimplemented methods raise
NotImplementedError.

[snip]

 > Overloading ``isinstance()`` and ``issubclass()``
 > -------------------------------------------------

Perhaps the PEP should just be reduced to include only this section.

[snip]

 > The ``abc`` Module: an ABC Support Framework
 > --------------------------------------------
[snip]
 > These methods are intended to be be called on classes whose metaclass
 > is (derived from) ``ABCMeta``; for example::
 >
 >     from abc import ABCMeta

     import zope.interface

 >     class MyABC(metaclass=ABCMeta):
 >         pass

     class MyInterface(zope.interface.Interface):
         pass

 >     MyABC.register(tuple)

     zope.interface.classImplements(tuple, MyInterface)

 >     assert issubclass(tuple, MyABC)

     assert MyInterface.implementedBy(tuple)

 >     assert isinstance((), MyABC)

     assert MyInterface.providedBy(())


 > The last two asserts are equivalent to the following two::
 >
 >     assert MyABC.__subclasscheck__(tuple)
 >     assert MyABC.__instancecheck__(())
 >
 > Of course, you can also directly subclass MyABC::
 >
 >     class MyClass(MyABC):
 >         pass

     class MyClass:
         zope.interface.implements(MyInterface)

 >     assert issubclass(MyClass, MyABC)

     assert MyInterface.implementedBy(MyClass)

 >     assert isinstance(MyClass(), MyABC)

     assert MyInterface.providedBy(MyClass())

 > Also, of course, a tuple is not a ``MyClass``::
 >
 >     assert not issubclass(tuple, MyClass)
 >     assert not isinstance((), MyClass)
 >
 > You can register another class as a subclass of ``MyClass``::
 >
 >     MyClass.register(list)

There is an interface that MyClass implements that list implements as well.

     class MyClassInterface(MyInterface):
         pass

     zope.interface.classImplements(list, MyClassInterface)

Sidebar: this highlights one of the reasons zope.interface users employ
the naming convention of prefixing their interface names with "I", it
helps keep interface names short while giving you an easy name for
"interface that corresponds to things of class Foo", which would be
IFoo.

 >     assert issubclass(list, MyClass)

     assert MyClassInterface.implementedBy(list)

 >     assert issubclass(list, MyABC)

     assert MyClassInterface.extends(MyInterface)

 > You can also register another ABC::
 >
 >     class AnotherClass(metaclass=ABCMeta):
 >         pass

     class AnotherInterface(zope.interface.Interface):
         pass

 >     AnotherClass.register(basestring)

     zope.interface.classImplements(basestring, AnotherInterface)

 >     MyClass.register(AnotherClass)

I don't quite understand the intent of the above line.  It appears to be
extending the contract that AnotherClass embodies to promise to fulfill
any contract that MyClass embodies.  That seems to be an unusual thing
to want to express.  Although unusual, you could still do it using
zope.interface.  One way would be to add MyClassInterface to the
__bases__ of AnotherInterface.

OTOH, I might be confused by the colapsing of the class and interface
hierarchies.  Do the classes in the above line of code represent the
implementation or specification?

[snip]

 > ABCs for Containers and Iterators
 > ---------------------------------

zope.interface defines similar interfaces.  Surprisingly they aren't
used all that often.  They can be viewed at
http://svn.zope.org/zope.interface/trunk/src/zope/interface/common/.
The files mapping.py, sequence.py, and idatetime.py are the most
interesting.

[snip rest]

> I was just
> thinking of how to "sell" ABCs as an alternative to current happy
> users of zop.interfaces.

One of the things that makes zope.interface users happy is the 
separation of specification and implementation.  The increasing 
separation of specification from implementation is what has
driven Abstract Data Types in procedural languages, encapsulation
in OOP, and now zope.interface.  Mixing the two back together in
ABCs doesn't seem attractive.

As for "selling" current users on an alternative, why bother?  If people 
need interfaces, they know where to find them.  I suspect I'm confused 
as to the intent of this discussion.
-- 
Benji York
http://benjiyork.com


More information about the Python-3000 mailing list