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

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