[Python-3000] ABC's, Roles, etc

Guido van Rossum guido at python.org
Sat May 12 21:19:58 CEST 2007


On 5/12/07, Jeff Shell <eucci.group at gmail.com> wrote:
> I like that the interface hierarchy is different than an
> implementation hierarchy. I like that it's easier to test for
> interface provision than it is to use isinstance() -
> `IFoo.providedBy(obj)` often works regardless of whether 'obj' is a
> proxy or wrapper, and without tampering with `isinstance()`. I know
> that there's been talk of having ``__isinstance()__`` and
> ``__issubclass()__``, which could be used to take care of the
> proxy/wrapper problem. But I haven't formed an opinion about how I
> feel about that.
>
> I like the Roles/Traits side of zope.interface because I can declare
> that information about third party products. For example, I was able
> to add some 'implements' directives to a SQLAlchemy 'InstrumentedList'
> class - basically I said that it supported the common `ISequence`
> interface. Which I recognize that in this particular scenario, if that
> role/trait or abstract base class was built in, than I wouldn't have
> had to do that (since it is based on a common Python type spec). Still
> though - it doesn't matter whether `InstrumentedList` derives from
> `list` or `UserList` or implements the entire sequence API directly.
> The Trait could be assigned independent of implementation, and could
> be done in another product without affecting any internals of
> SQLAlchemy: I didn't have to make a subclass that SQLAlchemy wouldn't
> know to instantiate. I didn't have to write an adapter. I just had to
> say "I happen to know that instances of this class will have this
> trait".
>
> I don't know if that's LBYL, EYV (Eat Your Vegetables), LBWBCTS (Look
> Both Ways Before Crossing The Street), or what. I think it's just a
> way of saying "I happen to know that this thing smells like a duck. It
> doesn't say that it smells like a duck, but I know it smells like a
> duck. And for everywhere that I expect to find the fine fragrance of
> duck, this thing should be allowed." No adapters, no changing the base
> classes, no meddling with method resolution order, just adding a
> trait. The trait in this case is like an access pass - just an extra
> thing worn around the neck that will grant you access to certain doors
> and pathways. It doesn't change who you are or how you accomplish your
> job.

Please have a look at the latest version (updated yesterday) of PEP
3119. Using the classes there, you can say

  from collections import Sequence
  Sequence.register(InstrumentedList)

>From this point, issubclass(InstrumentedList, Sequence) will be true
(and likewise for instances of it and isinstance(x, Sequence)). But
InstrumentedList's __mro__ and __bases__ are unchanged. This is pretty
close to what you expect from a Zope interface, except you can also
subclass Sequence if you want to, and a later version of SQLAlchemy
could subclass InstrumentedList from Sequence. (The register() call
would then be redundant, but you won't have to remove it -- it will
act as a no-op if the given subclass relationship already holds.)

A subclass of Sequence can behave either as an implementation class
(when it provides implementations of all required methods) or as
another interface (if it adds one or more new abstract method). You
can think of Sequence and its brethren as mix-ins -- they provide some
default implementations of certain methods, and abstract definitions
of others (the "essential" ones; e.g. Sequence makes __len__ and
__getitem__ abstract but __iter__ has a concrete default
implementation).

Phillip has told me that this is transparent to his GF machinery -- if
you overload a GF on Sequence, and InstrumentedList is a subclass
Sequence (whether through registration or subclassing), then that
version of the GF will be used for InstrumentedList (unless there's a
more specific overloaded version of course).

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


More information about the Python-3000 mailing list