[Python-3000] [Python-Dev] PEP 367: New Super

Guido van Rossum guido at python.org
Sat May 26 01:13:17 CEST 2007


On 5/25/07, Tim Delaney <timothy.c.delaney at gmail.com> wrote:
> Bah - this should have gone to Pyton-3000 too, since it's discussing the
> PEP.

My fault; I started sending you feedback that only went to you, Calvin
and the PEP editors. I've added python-3000 at python.org back here.

> Guido van Rossum wrote:
>
> > - This seems to be written from the POV of introducing it in 2.6.
> > Perhaps the PEP could be slightly simpler if it could focus just on
> > Py3k? Then it's up to the 2.6 release managers to decide if and how to
> > backport it.
>
> That was my original intention, but it was assigned a non-Py3k PEP number,
> so I presumed I'd missed an email where you'd decided it should be for 2.6.
>
> We should probably change the PEP number if it's to be targetted at Py3K
> only.

Maybe. There are a bunch of PEPs that were originally proposed before
the Py3k work started but that are now slated for inclusion in 3.0. I
don't think we should renumber all of those.

> > - Why not make super a keyword, instead of just prohibiting assignment
> > to it? (I'm planning to do the same with None BTW in Py3k -- I find
> > the "it's a name but you can't assign to it" a rather silly business
> > and hardly "the simplest solution".)
>
> That's currently an open issue - I'm happy to make it a keyword - in which
> case I think the title should be changed to "super as a keyword" or
> something like that.

As it was before. :-)

What's the argument against?

> > - "Calling a static method or normal function that accesses the name
> > super will raise a TypeError at runtime." This seems too vague. What
> > if the function is nested within a method? Taking the specification
> > literally, a nested function using super will have its own preamble
> > setting super, which would be useless and wrong.
>
> I'd thought I'd covered that with "This name behaves
> identically to a normal local, including use by inner functions via a cell,
> with the following exceptions:", but re-reading it it's a bit clumsy.
>
> The intention is that functions that do not have access to a 'super' cell
> variable will raise a TypeError. Only methods using the keyword 'super' will
> have a preamble.
>
> Th preamble will only be added to functions/methods that cause the 'super'
> cell to exist i.e. for CPython have 'super' in co.cellvars. Functions that
> just have 'super' in co.freevars wouldn't have the preamble.

I think it's still too vague. For example:

class C:
  def f(s):
    return 1
class D(C):
  pass
def f(s):
  return 2*super.f()
D.f = f
print(D().f())

Should that work? I would be okay if it didn't, and if the super
keyword is only allowed inside a method that is lexically inside a
class. Then the second definition of f() should be a (phase 2)
SyntaxError.

Was it ever decided whether the implicitly bound class should be:

- the class object as produced by the class statement (before applying
class decorators);
- whatever is returned by the last class decorator (if any); or
- whatever is bound to the class name at the time the method is invoked?

I've got a hunch that #1 might be more solid; #3 seems asking for trouble.

There's also the issue of what to do when the method itself is
decorated (the compiler can't know what the decorators mean, even for
built-in decorators like classmethod).

> > - "For static methods and normal functions, <class> will be None,
> > resulting in a TypeError being raised during the preamble." How do you
> > know you're in this situation at run time? By the time the function
> > body is entered the knowledge about whether this was a static or
> > instance method is lost.
>
> The preamble will not technically be part of the function body - it occurs
> after unpacking the parameters, but before entering the function body, and
> has access to the C-level variables of the function/method object. So the
> exception will be raised before entering the function body.
>
> The way I see it, during class construction, a C-level variable on the
> method object would be bound to the (decorated?) class. This really needs to
> be done as the last step in class construction if it's to bind to the
> decorated class - otherwise it can be done as the methods are processed.

We could make the class in question a fourth attribute of the (poorly
named) "bound method" object, e.g. im_class_for_super (im_super would
be confusing IMO). Since this is used both by instance methods and by
the @classmethod decorator, it's just about perfect for this purpose.
(I would almost propose to reuse im_self for this purpose, but that's
probably asking for subtle backwards incompatibilities and not worth
it.)

Then when we're calling a bound method X (bound either to an instance
or to a class, depending on whether it's an instance or class method),
*if* the im_class_for_super is set, and *if* the function (im_func)
has a "free variable" named 'super', *then* we evaluate
__builtin__.__super__(X.im_class_for_super, X.im_self) and bind it to
that variable. If there's no such free variable, we skip this step.
This step could be inserted in call_function() in Python/ceval.c in
the block starting with "if (PyMethod_check(func) && ...)". It also
needs to be inserted into method_call() in Objects/classobject.c, in
the toplevel "else" block. (The ceval version is a speed hack, it
inlines the essence of method_call().)

Now we need to modify the compiler, as follows (assume super is a keyword):

- Consider three types of scopes, which may be nested: the outermost
(module or exec) scope, class scope, and function scope. The latter
two can be nested arbitrarily.

- The super keyword is only usable in an expression (it becomes an
alternative for 'atom' in the grammar). It can not be used as an
assignment target (this is a phase 2 SyntaxError) nor in a nonlocal
statement.

- The super keyword is only allowed in a function that is contained in
a class (directly or nested inside another function). It is not
allowed directly in a class, nor in the outermost scope.

- If a function contains a valid use of super, add a free variable
named 'super' to the function's set of free variables.

- If the function is nested inside another function (not in a class),
add the same free variable to that outer function too, and so on,
until a function is reached that is nested in a class, not in a
function.

- All *uses* of the super keyword are turned into references to this
free variable.

I think this should work; it mostly uses existing machinery; it is
explainable using existing mechanisms.

If a function using super is somehow called without going through the
binding of super, it will just get the normal error message when super
is used:

NameError: free variable 'super' referenced before assignment in enclosing scope

IMO that's good enough; it's pretty hard to produce such a call.

> I was thinking that by binding that variable to Py_None for static methods
> it would allow someone to do the following:
>
> def modulefunc(self):
>     pass
>
> class A(object):
>     def func(self):
>         pass
>
>     @staticmethod
>     def staticfunc():
>         pass
>
> class B(object):
>     func = A.func
>     staticfunc = A.staticfunc
>     outerfunc = modulefunc
>
> class C(object):
>     outerfunc = B.outerfunc
>
> but that's already going to cause problems when you call the methods - they
> will be being called with instances of the wrong type (raising a TypeError).

I don't see any references to super in that example -- what's the relevance?

> So now I think both static methods and functions should just have that
> variable left as NULL. Trying to get __super__(NULL) will throw a TypeError.

See my proposal above. It differs slightly in that the __super__ call
is made only when the class is not NULL. On the expectation that a
typical function that references super uses it exactly once per call
(that would be by far the most common case I expect) this is just
fine. In my proposal the 'super' variable contains whatever
__super__(<class>, <inst>) returned, rather than <class> which you
seem to be proposing here.

> > - The reference implementation (by virtue of its bytecode hacking)
> > only applies to CPython. (I'll have to study it in more detail later.)
>
> Yep, and it has quite a few limitations. I'd really like to split it out
> from the PEP itself, but I'm not sure where I should host it.

Submit it as a patch to SourceForge and link to it from the PEP (I did
this for PEP 3119). If you still care about it -- I'm also okay with
just having it in the subversion archives.

> > I'll probably come up with more detailed feedback later. Keep up the
> > good work!!
>
> Now I've got to find the time to try implementing it. Neal has said he's
> willing to help, but I want to give it a go myself.

Great (either way) !

PS if you like my proposal, feel free to edit it into shape for the PEP.

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


More information about the Python-3000 mailing list