[Python-3000] Breakthrough in thinking about ABCs (PEPs 3119 and 3141)

Guido van Rossum guido at python.org
Tue May 1 02:19:27 CEST 2007


After a couple of whiteboard discussions with Collin Winter and
Jeffrey Jasskin I have a much better grip on where to go next with the
ABC PEPs.

(a) Roles

Collin will continue to develop his Roles PEP. This may or may not end
up providing a viable alternative to ABCs; in either case it will be
refreshing to compare and contrast the two proposals.

(b) Overloading isinstance and issublcass

The idea of overloading isinstance and issubclass is running into some
resistance. I still like it, but if there is overwhelming discomfort,
we can change it so that instead of writing isinstance(x, C) or
issubclass(D, C) (where C overloads these operations), you'd have to
write something like C.hasinstance(x) or C.hassubclass(D), where
hasinstance and hassubclass are defined by some ABC metaclass. I'd
still like to have the spec for hasinstance and hassubclass in the
core language, so that different 3rd party frameworks don't need to
invent different ways of spelling this inquiry.

Personally, I still think that the most uniform way of spelling this
is overloading isinstance and issubclass; that has the highest
likelihood of standardizing the spelling for such inquiries. I'd like
to avoid disasters such as Java's String.length vs. Vector.length()
vs. Collection.size(). One observation is that in most cases
isinstance and issubclass are used with a specific, known class as
their second argument, so that the likelihood of breaking code by this
overloading is minimal: the calls can be trusted as much as you trust
the second argument. (I found only 4 uses of isinstance(x, <variable>)
amongst the first 50 hits in Google Code Search.)

However this turns out, it makes me ant to reduce the number of ABCs
defined initially in the PEPs, as it will now be easy to define ABCs
representing "less-powerful abstractions" and insert them into the
right place in the ABC hierarchy by overloading either issubclass or
the alternative hassubclass class method.

(c) ABCs as classes vs. ABCs as metaclasses

The original ABC PEPs naively use subclassing from ABCs only. This ran
into trouble when someone observed that if classes C and D both
inherit from TotallyOrdered, that doesn't mean that C() < D() is
defined. (For a quick counterexample, consider that int and str are
both total orders, but 42 < "a" raises TypeError.) Similar for Ring
and other algebraic notions introduced in PEP 3141. The correct
approach is for TotallyOrdered to be a metaclass (is this the
typeclass thing in Haskell?). I expect that we'll leave them out of
the ABC namespace for now and instead just spell out __lt__ and __le__
as operators defined by various classes. If you want TotallyOrdered,
you can easily define it yourself, call TotallyOrdered.register(int)
etc., and then isinstance(int, TotallyOrdered) (or
TotallyOrdered.hasinstance(int)) will return True. OTOH, many of the
classes proposed in PEP 3119 (e.g. Set, Sequence, Mapping) do make
sense as base classes, and I don't expect to turn these into
metaclasses.

(d) Comparing containers

I am retracting the idea of making all sequences comparable; instead,
you can compare only list to list, tuple to tuple, str to str, etc.
Ditto for concatenation. This means that __eq__ and __and__ are not
part of the Sequence spec.

OTOH for sets, I think it makes sense to require all set
implementations to be inter-comparable: an efficient default
implementation can easily be provided, and since sets are a relatively
new addition, there is no prior art of multiple incompatible set
implementations in the core; to the contrary, the two built-in set
types (set and frozenset) are fully interoperable (unlike sequences,
of which there are many, and none of these are interoperable).

For mappings I'm on the fence; while it would be easy to formally
define m1 == m2 <==> set(m1.items()) == set(m2.items()), and that
would be relatively easy to compute using only traversal and
__getitem__, I'm not so sure there is any use for this, and it does
break with tradition (dict can't currently be compared to the various
dbm-style classes).

(e) Numeric tower

Jeffrey will write up the detailed specs for the numeric tower.
MonoidUnderPlus, Ring and other algebraic notions are gone. We will
have abstract classes Integer <: Rational <: Real <: Complex <: Number
(*); and concrete classes complex <: Complex, float <: Real,
decimal.Decimal <: Real, int <: Integer. (No concrete implementations
of Rational, but there are many 3rd prty ones to choose from.) We came
up with a really clever way whereby the implementations of binary
operations like __add__ and __radd__ in concrete classes should defer
to their counterpart in the abstract base class instead of returning
NotImplemented; the abstract base class can then (at least in most
cases) do the right thing when mixed operations are attempted on two
different concrete subclasses that don't know about each other
(solving a dilemma about which I blabbered yesterday). This does mean
that Integer...Number will be built-in.

(*) D <: C means that D is a subclass of C.

-- 
--Guido van Rossum (home page: http://www.python.org/~guido/)


More information about the Python-3000 mailing list