Ich habe einen Fall, in dem meine Klasse eine benutzerdefinierte Metaklasse hat, die beim Erstellen eine Klassenmethode der Klasse aufruft, etwa:

class Metaclass(type):
    def __new__(cls, name, bases, attrs):
        ...
        new_class = super(Metaclass, cls).__new__(cls, name, bases, attrs)
        ...
        new_class.get_fields() # do something
        ...
        return new_class

class FooBar(object):
    __metaclass__ = Metaclass

    @classmethod
    def get_fields(cls):
        ...

(Ein Beispiel für einen solchen Code finden Sie in Tastypie.)

Das Problem ist, wenn ich tun möchte:

class NewBar(FooBar):
    @classmethod
    def get_fields(cls):
        super(NewBar, cls).get_fields()
        ...

Dies funktioniert nicht, da NewBar zum Zeitpunkt des Aufrufs von super noch nicht erstellt wurde (der Programmablauf befindet sich noch in der Metaklasse). Gibt es eine Problemumgehung?

Ich weiß, dass die get_fields -Methode wahrscheinlich zu einer Metaklassenmethode werden könnte, aber dies würde die Implementierung der Vererbung erheblich erschweren (Sie müssten sowohl die neue Metaklasse als auch die Klasse selbst definieren, was für Entwickler, die diese Klassen erweitern möchten, nicht hilfreich ist).

(Python 2.7.)

8
Mitar 6 Okt. 2012 im 10:48

3 Antworten

Beste Antwort

Wenn NewBar beim Aufrufen von get_fields nicht verfügbar sein kann, finden Sie es weiterhin im MRO von cls:

@classmethod
def get_fields(cls):
    # we can get invoked before NewBar is available in globals,
    # so get NewBar from cls.__mro__
    NewBar = next(c for c in cls.__mro__
                  if c.__module__ == __name__ and c.__name__ == 'NewBar')
    super(NewBar, cls).get_fields()
    ...

Obwohl dieser Code lustig aussieht, funktioniert er korrekt und ist erheblich einfacher als die in der Frage vorgeschlagenen Alternativen. Während die meisten Aufrufe von super mit einem nicht konstanten ersten Argument (z. B. unqualifiziertes super(cls, cls)) falsch sind und die Vererbung unterbrechen, ist dieser sicher, da der Generatorausdruck nichts anderes als eine unkonventionelle Methode ist, um einen Halt zu erhalten von NewBar.

Bei der Suche nach den Klassen im MRO suchen wir nach Klassen- und Modulnamen (verfügbar als __name__, wie von Mitar hervorgehoben), um ein falsches Positiv zu vermeiden, wenn othermodule.NewBar von thismodule.NewBar erbt.

3
Community 23 Mai 2017 im 11:43

Basierend auf der Antwort von @ user4815162342 fand ich eine noch einfachere Lösung:

try:
    super(NewBar, cls).get_fields()
except NameError, e:
    if 'NewBar' in str(e):
        super(cls, cls).get_fields()
    else:
        raise
0
Mitar 6 Okt. 2012 im 08:09

Ich weiß, dass die Frage spezifisch für Python 2.7 ist. Für diejenigen, die Python 3.6 verwenden, können Sie einfach super() aufrufen.

class NewBar(FooBar):
    @classmethod
    def get_fields(cls):
        super().get_fields()
        ...
0
Sherpa 7 Sept. 2017 im 09:19