[Python-3000] PEP 3100 Comments

Bill Janssen janssen at parc.com
Thu May 11 01:30:21 CEST 2006


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).

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.)

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)
> 
> 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.

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.

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.

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.

Bill


More information about the Python-3000 mailing list