[Python-3000] Revised PEP 3119 (Abstract Base Classes) (original) (raw)

Talin talin at acm.org
Sat May 12 21:07:24 CEST 2007


Guido van Rossum wrote:

Here's a new version of the ABC PEP. A lot has changed; a lot remains. I can't give a detailed overview of all the changes, and a diff would show too many spurious changes, but some of the highlights are:

Some general comments on the PEP:

Compared to the previous version, this version of the PEP is closer in spirit to the various other competing proposals for 'post-hoc object taxonomies', although some important differences remain. I'd like to point out both the similarities and the differences, especially the latter as they form the basis for further discussion and possibly evolution.

First, the ways in which the new PEP more closely resembles its competitors:

The new version of the PEP is more strongly oriented towards post-hoc classification of objects, in other words, putting classes into categories that may not have existed when the classes were created.

It also means that there is no longer a requirement that categories for built-in objects have an official Python "seal of approval". Anyone can come along and re-categorize the built-ins however they like; And they can do so in a way that doesn't interfere with any previously existing categories. There will of course be certain 'standard' categories (as outlined in the PEP), but these standard categories do not have any privileged status, unlike the ones in the earlier versions of the PEP.

It means that if we make a mistake defining the categories (or more likely, if we fail to address someone's needs), it is possible for someone else to come along and repair that mistake by defining a competing taxonomy.

The categorization relationships are now stored preferentially in a map which is external to the objects being categorized, allowing objects to be recategorized without mutating them. This is similar to the behavior of Colin Winter's 'roles' proposal and some others.

(For the remainder of this document, I am going to use the term "dynamic inheritance" to describe specifying inheritance via Guido's special methods, as opposed to "traditional inheritance", what we have now.)

Now, on to the differences:

The key differentiator between Guido's proposal and the others can be summarized by the following question: "Should the mechanism which defines the hierarchy of classes be the same as the mechanism that defines the hierarchy of categories?"

To put it another way, is a "category" (or "interface" or "role" or whatever term you want to use) a "class" in the normal sense, or is it some other thing?

In the terminology of Java and C# and other languages which support interfaces, the term 'interface' is explicitly defined as something that is 'not a class'. A class is a unit of implementation, and interfaces contain no implementation. [1]

In these object classification systems, there are three different relationships we care about:

-- The normal inheritance relationship between classes.

-- The specification of which classes belong to which categories.

-- The relationship between the categories themselves.

(Note that in some systems, such as Raymond Hettinger's attribute-based proposal, the third type of relationship doesn't exist - each category is standalone, although you can simulate the effects of a category hierarchy by putting objects in multiple categories. Thus, there's no MutableSequence category, but you can place an object in both Mutable and Sequence and infer from there.)

Given these different types of relationships, the question to be asked is, should all of these various things use the same mechanism and the same testing predicate (isinstance), or should they be separate mechanisms?

I'll try to summarize some of the pros and cons, although this should not be considered a comprehensive list:

Arguments in favor of reusing 'isinstance':

-- It's familiar and easy to remember.

-- Not everyone considers interfaces and implementations to be 

distinct things, at least not in Python where there are no clear boundaries enforced by the language (as can be seen in Guido's desire to have some partial implementation in the ABCs.)

-- Declaring overloads in PJE's generic function proposal is cleaner 

if we only have to worry about annotating arguments for types rather than types + interfaces. In other words, we would need two different kinds of annotations for a given method signature, and a way to discriminate between them. If categories are just base classes, then we only have one dispatch type to worry about. [2]

Arguments in favor of a different mechanism:

-- Mixing different kinds of inheritance mechanisms within a single 

object might lead to some strange inconsistencies. For example, if you have two classes, one which derives from an ABC using traditional inheritance, and one which derives using dynamic inheritance, they may behave differently.

(For example, the @abstractmethod decorator only affects classes 

that derive from the ABC using traditional inheritance, not dynamic inheritance. Some folks my find this inconsistency objectionable.)

-- For some people, an interface is not the same thing as a class, 

and should not be treated as such. In particular, there is a desire by some people to enforce a stricter separation between interface and implementation.

-- Forcing them to be separate allows you to make certain 

simplifying assumptions about the class hierarchy. If categories can relate to each other via traditional inheritance, and if I want to trace upwards from a given class to find all interfaces that it implements, then I may have to trace both traditional and dynamic inheritance links. If categories can only relate via some special scheme, however, then I can simply do my tracing in two passes: First find all base classes using traditional inheritance, and then given that set, find all categories using dynamic inheritance. In other words, I don't have to keep switching inheritance types as I trace.


[1] On the other hand, both C# and Java allow interfaces to be tested by their equivalent of "isinstance" so there is some conflation of the two. On the gripping hand, however, C# and Java are both statically typed language, where things like "isinstance" really means "istype", whereas in Python "isinstance" really means something more like "isimplementation". So there is no exact equivalent to what Python does here.

[2] I should mention that one of my personal criteria for evaluating these proposals is the level of synergy achieved with PJE's PEP. Now, PJE may claim that he doesn't need interfaces or ABCs or anything, but I believe that his PEP benefits considerably by the existence of ABCs, because it means that you need far fewer overloads in an ABC world. Thus, I can overload based on "Sequence" rather than having to have separate overloads for list, tuple, and various user-created sequence types. (Although I can if I really need to.)

I would go further, and say that these object taxonomies should only go so far as to provide what is needed to obtain that synergy; Any features beyond that are mostly superfluous. But that's just my personal opinion.

-- Talin



More information about the Python-3000 mailing list