[Python-de] super()

Christian Tanzer tanzer at swing.co.at
Wed Sep 4 08:48:10 EDT 2002


Thomas Fanslau <tfanslau at gmx.de> wrote:

> Ich bin am Wochenende über die Funktion 'super()' gestolpert. Zuerst
> habe ich mich gefreut und gedacht ich könnte damit einige Konstrukte in
> meinen Sourcen entfernen. Aber irgendwie macht 'super()' gar nicht das
> was ich gedacht habe. Ja, ich möchte sogar fast sagen, ich finde die
> Funktion völlig überflüssig und eher sogar noch gefährlich.
(snip)
> 'super()' macht eigentlich nichts davon einfacher. Will ich eine Klasse
> speichern (triviales Beispiel)
>
> class A(object):
>       def save(self):
>               print "a"
>
> class B(object):
>       def save(self):
>               print "b"
>
> class D(B, A):
>       def save(self):
>               super(self.__class__, self).save()
>               print "d"
>
> dann wird nur die ERSTE gefundene Funktion aufgerufen. Füge ich beim
> Refactoring eine weitere Basisklasse hinzu oder vertausche die
> Reihenfolge der Klassen, dann läuft der Code wie gehabt weiter und ich
> stosse erst später auf den Fehler. Will ich aber die zweite
> 'save()'-Funktion aufrufen, dann muss ich das wieder explizit über den
> Namen der Superclasse tun. Wird jetzt die Reihenfolge der Klassen
> getauscht, dann wird ggf. dieselbe Funktion zweimal aufgerufen!!!
>
> Übersehe ich hier was wichtiges, oder ist es wirklich so schlimm?

Du hast `super` nicht richtig verstanden/verwendet.

So gehts:

class R(object) :
    def save(self):
        pass

class A(R):
    def save(self):
        super(A, self).save()
        print "a"

class B(R):
    def save(self):
        super(B, self).save()
        print "b"

class D(B, A):
    def save(self):
        super(D, self).save()
        print "d"

Warum? `super(Class, object)` liefert aus `object.__class__.mro()` das
auf `Class` folgende Element. Anhand des obigen Beispiels:

`D.mro()` ist die Liste `(D, B, A, R, object)`. Wenn Du eine Instanz von
`D` hast (nennen wir's `d`) und `d.save` aufrufst, passiert folgendes:

- Zuerst wird `D.save` aufgerufen.

- `super(D, self)` liefert `B`, daher wird als nächstes `B.save`
  aufgerufen.

- `super(B, self)` liefert `A`, daher wird als nächstes `A.save`
  aufgerufen.

- `super(A, self)` liefert `R`, daher wird als nächstes `R.save`
  aufgerufen.

- `R.save` enthält keinen `super` Aufruf, daher ist hier Schluß.

Wichtig dabei ist, daß in den `super` Aufrufen die Klasse immer
Literal drinnen steht -- ein `self.__class__` im Aufruf würde bei der
obigen Vererbungshierarchie zu einer Endlosschleife führen.

Nachdem der literale Klassenname im `super` Aufruf unwillkommen ist,
empfiehlt es sich, eine Metaklasse einzusetzen, die das ganze
komfortabler macht:

class Autosuper(type) :
    """Metaclass adding a private class variable `__super` containing
       `super(cls)`.

       `__super` can be used for cooperative method calls. For instance:

           def foo(self, bar, baz) :
               ...
               self.__super.foo(bar, baz)
               ...
    """

    def mangled_name(cls, name) :
        """Returns `name` as mangled by Python for occurences of `__%s` % name
           inside the definition of a class `cls`.
        """
        if cls.__name__.startswith("_") :
            format = "%s__%s"
        else :
            format = "_%s__%s"
        return format % (cls.__name__, name)

    def __init__(cls, name, bases, dict) :
        super(Autosuper, cls).__init__(name, bases, dict)
        setattr(cls, cls.mangled_name("super"), super(cls))

Damit würde obiges Beispiel so aussehen:

class R(object) :
    __metaclass__ = Autosuper
    def save(self):
        pass

class A(R):
    def save(self):
        self.__super().save()
        print "a"

Cool, oder?

-- 
Christian Tanzer                                         tanzer at swing.co.at
Glasauergasse 32                                       Tel: +43 1 876 62 36
A-1130 Vienna, Austria                                 Fax: +43 1 877 66 92





More information about the Python-de mailing list