[Python-Dev] New Super PEP - draft 2 (original) (raw)

Jim Jewett jimjjewett at gmail.com
Sun Apr 29 23:15:33 CEST 2007


(Adding python-3000 to the Cc, since that is where much of the recent discussion occurred)

Calvin's full proposal is archived at: http://mail.python.org/pipermail/python-dev/2007-April/072835.html

Abstract ========

The PEP defines the proposal to enhance the super builtin to work implicitly upon the class within which it is used and upon the instance the current function was called on. The premise of the new super usage suggested is as follows:

super.foo(1, 2)

to replace the old:

super(Foo, self).foo(1, 2)

The alternatives section needs to explain why "super.foo(*args)" is the right level of simplification. Alternatives include

(1) No changes

Con: fragility in name binding

(2) super(this_class, self).foo(*args)

Con: The "(this_class, self)" is boilerplate.

Note that the this_class PEP should be referenced, if only as a possible explanation for what is happening under the covers.

(3) self.super.foo(*args) # or super.foo(*args)

Con: Shouldn't need a double_underscore_name in normal functions. Con: super (even if renamed super) is not a simple attribute; it is a property representing a partially applied function (that gets further applied by the "self")

(4) super(self, *args) # ? or super(self, *args)

Actually, I sort of like this one, as it allows the upcall signature to exactly match the method definition signature.

Con: super is still not a simple attribute. Con: changing the method name becomes a hassle.

(5) super.foo(self, *args) # ? or super.foo(self, *args)

Con: self isn't really an argument just to super.foo -- it is an argument to super which is used to find/instantiate foo in the first place. (Plus the objections to partially applied function attributes.)

(6) super # ? or super()

Pro: matches java; doesn't require a repeat of *args Con: doesn't match anything else in python, needs to be a keyword (and would still need object support).

In the first example:

    class A(object):
        def f(self):
            return 'A'

    class B(A):
        def f(self):
            return 'B' + super.f()

    class C(A):
        def f(self):
            return 'C' + super.f()

    class D(B, C):
        def f(self):
            return 'D' + super.f()

    assert D().f() == 'DBCA'

You should probably include tests that fail with some of the more obvious (but wrong) solutions, such as

    class A(object):
        def f(self):
            return 'A'

    class Bempty(A): # Make sure it doesn't call the A or B method twice
        pass

    class B(Bempty):
        def f(self):
            return 'B' + super.f()

    class C(A):
        def f(self):
            return 'C' + super.f()

    class D(B, C):
        def f(self):
            return 'D' + super.f()

    assert D().f() == 'DBCA'

    class E(C, B):    # Show that B can point to C as next or vice versa
        def f(self):
            return 'E' + super.f()

    assert E().f() == 'ECBA'

    class F(D):    # show that the instance may not be a direct instance
        pass

    assert D().f() == 'DBCA'

The enhancements to the super type will define a new getattr classmethod of the super type,

Did you really mean, it gets the class of builtin.super, but no info on which class is using super? If not, it isn't a classmethod of the super type. It may be a classmethod of the currently-being-defined-type (this_class).

which must look backwards to the previous frame and locate the instance object.

frame manipulation is fragile. Even if it didn't cause problems for other implementations, it causes problems for nested functions and callbacks.

def f(self, button, *args):
    def callback(*args):
        super(__this_class__, self).f(*args)
    button.callback=callback

When this gets called, the appropriate self won't be in the frame, except as a lexically scoped variable (which might have a different name).

Also, note that this fails on most of Thomas Wouters' advanced usages. While you say that super shouldn't be called outside a method, you can't really keep the super object itself from getting returned.

"Every class will gain a new special attribute, super, which refers to an instance of the associated super object for that class" In this capacity, the new super also acts as its own descriptor, create an instance-specific super upon lookup.

If you didn't say instance-specific, this would be equivalent to a class decorator, translating

class A(object): ...

into

class A(object): ...

A.__super__=super(A)

As is, it gets a bit trickier, since you need to I think the translation is closer to

class A(object):...
    @property
    def __super__(self):
        return __this_class__.__supermaker()

A.__supermaker=super(A)

I don't see a good non-magical way to pass both the instance and the class-at-time-of-writing (as opposed to class of calling instance), which is why the other solutions used bytecode hacks or name mangling.

Much of this was discussed in the thread of the python-dev list, "Fixing super anyone?" [1].

I assume you meant the python-3000 list.

-jJ



More information about the Python-Dev mailing list