[Python-3000] PEP 3100 Comments

Guido van Rossum guido at python.org
Thu May 11 02:01:05 CEST 2006


On 5/10/06, Bill Janssen <janssen at parc.com> wrote:
> Guido writes:
> > The crux of the matter seems to be how to add behavior *after* you
> > already have an instance of a class -- or, equivalently (?), how to
> > add behavior to a class when you have no control over the code that
> > creates instances of it.
>
> I have no contribution to make about either of those, I think (though
> the compelling use cases elude me).

Thanks for the clarification! I'm sure Phillip and others have many
(of the latter form). Adaptation and generic functions are all about
that. It seems that this is one of the reasons you weren't getting
your point across.

(FWIW, I'm at best lukewarm on generic functions, despite having
implemented them several times now, and having proposed them as an
alternative to adaptation. They seem to be a useful trick for certain
esoteric situations, but I don't want to redesign everything to use
generic functions instead of classes, for example. Therefore, the
applicability of Haskell's typeclasses still eludes me.)

> My point was rather about the definition of the built-in and other
> standard types.  The Python community has evolved this culture of
> "magic method names", which I believe was primarily caused by the
> conflict between defined-in-C types and defined-in-Python types.
> People wanted a way to make things that looked like file objects, but
> couldn't actually subclass "file".  They wound up implementing a class
> that had some of the same method names that the built-in "file" object
> had: "read", "seek", "write", etc.  Usually not all of them, which
> would cause some problems when an instance of that type was used as a
> file object.  (I like the comment at the beginning of StringIO.py:
> "This implements (nearly) all stdio methods."  It's left as an
> exercise to the reader to determine what's missing.)

I'm not so sure that it had much to do with the inability to subclass.
Since subclassing also implies implementation inheritance, I'm not
sure how subclassing "file" would help StringIO. Now, if you add
abstract types into the mix, you could be on to something. It's still
radical, and I don't know how far the enforcement should go, and there
are issues with the need for splinter interfaces (e.g. the various
kinds of files: seekable or not, read-only/write-only/read-write --
that already produces 6 different interfaces); but it's a not entirely
unreasonable proposal to say "we call it a sequence if it inherits
from this public abstract base class AbstractSequence" and so on.
There might not be a requirement to implement all methods (this could
be a way to avoid too many splinters).

I think there's a lot to argue for and against this proposal (more
than I have time). But I understand it, it might work, and it
shouldn't change the way we code in Python too much (except for the
places where we mess with isinstance() or hasattr()).

> Worse, programs that wanted to do runtime type-checking or type-based
> dispatch had no accurate way to check types.  You'd have to crawl the
> whole outline of the type, and, even if you found the method names you
> were looking for, you had no way to know if their *meaning* was the
> same.  With a type-based system, you could check the type to see if
> (a) the methods you needed are there, and (b) if they have the
> semantics you need.  It's just shorthand for a group of methods along
> with their supposed meanings.
>
> Sure, it can be subverted, along with any other programming construct,
> by a programmer who inherits from the type, then proceeds to override
> methods or operators using completely different semantics:
>
>    class JarFile (ZipFile):
>
>      def listfiles(self):
>         import shutil
>         shutil.rmtree("/")
>
> tomer filiba makes a point:
> > in python, the type of x is the method-resolution-order (mro) of x, i.e.,
> > it defines what happens when you do x.y. (by the way, it would be
> > nice if __mro__ was mutable)

You can change it by using a metaclass. That should be good enough.

> > doing isinstance(a, b) only says it "a" has "b" in it's bases, not that "a"
> > compiles with "b"'s APIs or method signatures. there's no way to tell
> > the method signature is what you expect before calling it. therefore, i
> > see no reason why people should use type()/isinstance() on objects.

But the *convention* is that if it inherits from b it conforms to b's
interface and semantic, unless you are working with a framework that
has a way to spell exceptions to that rule (like inheritance for
implementation only).

> That's why people write "b.y(x)" if they have any doubt about the mro.
> To make sure you are calling the right method, with the right
> signature, with the right semantics.

In my experience this is rare except when explicitly calling the
method one is overriding in a single inheritance situation.

> I think there's some confusion about the differences between "duck
> typing", "dynamic typing", and "static typing", as well.  The choice
> isn't between Java typing (mainly static, and cumbersome) and no
> typing (which is what duck typing is close to).  I'm a big believer in
> dynamic typing (though optional partial static typing would be nice).
> Strong dynamic typing would be nice to have, but I'd be happy if we
> just discouraged the sloppy typing that's currently the vogue in
> Python programming.

I'm not sure you can legislate duck typing away though. If I write a function

def foo(f):
  f.write("Ha ha ha")

with the intention that it be called with a file instance, you can't
prevent me from calling it with an instance of this class:

class Fool:
  def write(self, blah): return blah

> Anyway...  Since the standard types can now be inherited from, I was
> suggesting that the Py3K core consist of a set of types, which are
> designed to be used, but also to be inherited from.  I was suggesting
> that StringIO, for instance, in Py3K should inherit from "file", and
> override methods as necessary to achieve its effect.  I was *not*
> suggesting that the ability to add slots to instances be removed, or
> that the ability to introspect over values be removed.

I see. At least it would solve the problem that there's no way to tell
if something is a sequence or a mapping when all it implements is
__getitem__, __len__ and __contains__ (to take an extreme example). It
could form a solid basis to define isSequence, and callable, too.

That's not a +1 or even a +0 (nor a -1 BTW). Just an acknowledgement
of understanding of what you're proposing.

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


More information about the Python-3000 mailing list