[Python-3000] pep 3124 plans
Phillip J. Eby
pje at telecommunity.com
Mon Jul 30 22:10:08 CEST 2007
At 12:20 PM 7/27/2007 -0400, Phillip J. Eby wrote:
>At 08:25 AM 7/27/2007 -0700, Guido van Rossum wrote:
> >Basic GFs, great. Before/after/around, good. Other method
> >combinations, fine. But GFs in classes and subclassing? Not until we
> >have a much better design.
>Sounds reasonable to me. The only time I actually use them in
>classes myself is to override existing generic functions that live
>outside the class, like ones from an Interface or a standalone generic.
>The main reason I included GFs-in-classes examples in the PEP is
>because of the "dynamic overloading" meme. In C++, Java, etc., you
>can use overloading in methods, so I wanted to show how you could do
>that, if you wanted to.
>I suspect that the simplest way to fix this in Py3K is with an
>"overloading" metaclass, as it would not even require any
>decorators. That is, you could provide a custom dictionary that
>records every definition of a function with the same name. The
>actual metaclass creation process would check for a method of the
>same name in a base class, and if it's generic (or the current class
>added more than one method), put a generic method in.
>With a little bit of work, you could probably determine whether you
>could get away with dropping the genericness in a subclass;
>specifically, if all the subclass-defined methods are "more specific"
>than all base class methods, then there's no need for them to be in
>the same generic function, unless they make next_method calls. Thus,
>you'll end up with normal methods except where absolutely necessary.
>Such a metaclass would make method overloads look pretty much the
>same as in OO languages with static overloading. The only remaining
>hole at that point would be reconciling super() and next_method. If
>you're using this metaclass, super() is only meaningful if you're not
>in the same generic function as is used in your base, while
>next_method() is only meaningful if you *are*.
>I don't know of any quick way to fix that, but I'll give it some thought.
I think I see how to resolve next_method() and super() now: if you
create a new GF in a subclass, you just define its default_action to
be something that calls super(). Then, you just use next_method()
instead of super().
Currently the default default_action is a NoMethodFound action, but
replacing it for a given GF is a piece of cake. So, an "overloading"
metaclass could be written that would:
1. Use __prepare__ to catch multiple function assignments to the same
name, converting them to overloads
2. Decide whether to combine those overloads with an existing generic
in the base classes, or to create a new generic and chain it with a
super() default action.
3. Automatically make the class object part of the overload
registrations for 'self'.
The principle downside to this approach is that only one metaclass
can provide a __prepare__ dictionary, which means it's even more
difficult to combine metaclasses than it is in today's Python --
which means I want to give a little more thought to PEP 3115, to see
if there is any way to at least emulate the "derived metaclass rule"
for __prepare__, that Python currently enforces for the base classes.
In other words, a class' metaclass has to be a derivative of all its
bases' metaclasses; ISTM that a __prepare__ namespace needs to be a
derivative in some sense of all its bases' __prepare__ results. This
probably isn't enforceable, but the pattern should be documented such
that e.g. the overloading metaclass' __prepare__ would return a
mapping that delegates operations to the mapping returned by its
super()'s __prepare__, and the actual class creation would be
similarly chained. PEP 3115 probably needs a section to explain
these issues and recommend best practices for implementing
__prepare__ and class creation on that basis. I'll write something
up after I've thought this through some more.
But I think this wraps up the overall question of *how* to integrate
methods and GFs in a way that supports a more C++/Java-like
overloading style (i.e., no decorators on individual overloads within
a class). The main drawback is that it's a silent error if you leave
off the metaclass.
Another option of course would be to make this part of the default
metaclass, but that would bring in the issue of needing a standard
API (and default implementation) for GF's.
In the meantime, though, it's nice to see a practical application for
PEP 3115 -- i.e., implementing transparent Java-style
overloading. It's absolutely not possible in 2.x without decorators,
both because of the lack of argument annotations and the lack of a
__prepare__-controlled class-suite namespace.
More information about the Python-3000