Wednesday, April 15, 2009

Zope Interfaces and Python Abstract Base Classes

Jim Fulton (the architect of Zope 2, Zope 3, zc.buildout, and so on) and I talked briefly on the first day of the sprints about whether we could take advantage of the Python 2.6/3.0 Abstract Base Classes (ABCs), either replacing Zope interfaces or somehow using them to our advantage.

So I don't misrepresent him, I'll state my conclusions. Ask Jim for his.

Unfortunately, I came to the conclusion that ABCs, as of their current implementation, are anemic and largely uninteresting from zope.component's perspective. Some points:

  • The basic ABC mechanism simply allows hooks into isinstance and issubclass, so that you can ask, for instance, ``isinstance(x, Y)``, and ``Y.__instancecheck__(x)`` is consulted, if the method exists. Jim said we could make zope.interface interfaces implement these hooks. That didn't seem very interesting to me: to me, ``isinstance(x, Y)`` is a very different semantic question than ``Y.providedBy(x)`` and the difference is valuable. Same for ``issubclass(X, Y)`` versus ``Y.implementedBy(X)``. I'd rather not have zope.interface muddy that water.
  • The abc module allows you to create abstract base classes. To declare that a class is a concrete implementation of the ABC, you can either have the concrete class subclass the ABC; or, you can call the ABC's ``register`` method. In the current implementation, the registration is one-way, stored on the ABC. Therefore, you can ask an ABC, via various internals, what its "subclasses" are; but you cannot ask a class or instance about the ABCs it subclasses or is an instance of. zope.component needs to be able to ask what a class implements, or what an instance provides.
  • The zope.interface implementation is highly optimized, and relies on cacheing results for speed. The abc module is not geared for speed.
  • I wondered if we could at least leverage the default Python ABCs, describing mappings and sequences, to create interfaces from them, deprecating the ones in zope.interface.common. Jim seemed disinterested in that, saying that he hadn't found ours useful. I have found them useful--as a shortcut to describing a contract based on a mapping or a sequence, usually--so that still seems like a possible win to me.

Jim pointed out that zope.interface scribbled its metadata on classes, which some people found distasteful, and at least the abc module didn't do that. This characteristic of ABCs might please some people. A global data structure might be another way for zope.interface to do its work while answering that concern, if that ever became something we wanted to solve.

So in conclusion, the basic mechanism and the module were not really of interest to me; I felt that the library of ABCs might at least be interesting, but Jim disagreed.

Too bad. But hey, maybe we can throw out the Zope testrunner in favor of nose! :-) Jim proposed that idea, and thought it might be doable, with a little extension trick to make nose support Zope layers....

3 comments:

Unknown said...

Why do you think that using isinstance/issubclass instead of providedBy/implementedBy would be "muddying the waters"? Both are used to basically check an object's behaviour (I include method signatures in "behaviour"), except for marker interfaces. At the syntactic I see no problems, and at the semantic level naming conventions ('I' prefix for interfaces) should cover most cases; when they don't, add a comment, or an assertion.

Several keywords and the concepts of "implementing" vs. "providing" would be jettissoned without functionality or readability loss -- sounds like a win to me.

Unknown said...

My "muddying the waters" concern was entirely semantic, not syntactic. I tentatively agree with you that there would be no syntactic problems.

However, semantically, it sounds like you are dismissing the differences in one hand and acknowledging them in the other. On the one hand, you seem to say that they are basically the same, because they describe an object's behavior. You are eager to jettison the concept of "providing". But on the other hand, you think that naming conventions, along with assertions and comments, will convey enough of the semantic differences--so you acknowledge they exist.

Are you arguing that the semantic difference between an instantiating class and a contract/interface is basically unimportant? Or that naming conventions should be enough for the initiated to recognize the difference, but the novices to safely ignore?

My position is that the semantic differences are real, and that they should be discoverable. Having different verbs for the concepts (isinstance vs. providedBy) helps novices recognize that something out of the ordinary is going on, and helps experts clearly read or state the intent. A naming convention is too easily dismissed.

Anonymous said...
This comment has been removed by a blog administrator.