Ich brauche einen funktionierenden Ansatz, um alle Klassen zu erhalten, die von einer Basisklasse in Python geerbt wurden.
Klassen neuen Stils (dh Unterklassen von object
, was in Python 3 die Standardeinstellung ist) haben eine __subclasses__
Methode, die die Unterklassen zurückgibt:
class Foo(object): pass
class Bar(Foo): pass
class Baz(Foo): pass
class Bing(Bar): pass
Hier sind die Namen der Unterklassen:
print([cls.__name__ for cls in Foo.__subclasses__()])
# ['Bar', 'Baz']
Hier sind die Unterklassen selbst:
print(Foo.__subclasses__())
# [<class '__main__.Bar'>, <class '__main__.Baz'>]
Bestätigung, dass die Unterklassen tatsächlich Foo
als Basis aufgeführt sind:
for cls in Foo.__subclasses__():
print(cls.__base__)
# <class '__main__.Foo'>
# <class '__main__.Foo'>
Hinweis: Wenn Sie Unterklassen möchten, müssen Sie Folgendes wiederholen:
def all_subclasses(cls):
return set(cls.__subclasses__()).union(
[s for c in cls.__subclasses__() for s in all_subclasses(c)])
print(all_subclasses(Foo))
# {<class '__main__.Bar'>, <class '__main__.Baz'>, <class '__main__.Bing'>}
Beachten Sie, dass wenn die Klassendefinition einer Unterklasse noch nicht ausgeführt wurde - beispielsweise wenn das Modul der Unterklasse noch nicht importiert wurde - diese Unterklasse noch nicht vorhanden ist und __subclasses__
sie nicht findet.
Sie haben "seinen Namen gegeben" erwähnt. Da Python-Klassen erstklassige Objekte sind, müssen Sie anstelle der Klasse oder dergleichen keine Zeichenfolge mit dem Klassennamen verwenden. Sie können die Klasse einfach direkt verwenden, und Sie sollten es wahrscheinlich tun.
Wenn Sie eine Zeichenfolge haben, die den Namen einer Klasse darstellt, und die Unterklassen dieser Klasse suchen möchten, gibt es zwei Schritte: Suchen Sie die Klasse mit ihrem Namen und suchen Sie dann die Unterklassen mit __subclasses__
wie oben.
Wie Sie die Klasse anhand des Namens finden, hängt davon ab, wo Sie sie erwarten. Wenn Sie erwarten, dass es sich im selben Modul befindet wie der Code, der versucht, die Klasse zu finden, dann
cls = globals()[name]
würde den Job machen, oder in dem unwahrscheinlichen Fall, dass Sie erwarten, ihn bei Einheimischen zu finden,
cls = locals()[name]
Wenn sich die Klasse in einem Modul befinden könnte, sollte Ihre Namenszeichenfolge den vollständig qualifizierten Namen enthalten - so etwas wie 'pkg.module.Foo'
statt nur 'Foo'
. Verwenden Sie importlib
diese Option , um das Modul der Klasse zu laden und dann das entsprechende Attribut abzurufen:
import importlib
modname, _, clsname = name.rpartition('.')
mod = importlib.import_module(modname)
cls = getattr(mod, clsname)
Wie auch immer Sie die Klasse finden, cls.__subclasses__()
würde dann eine Liste ihrer Unterklassen zurückgeben.
Wenn Sie nur direkte Unterklassen möchten,
.__subclasses__()
funktioniert dies einwandfrei. Wenn Sie alle Unterklassen, Unterklassen von Unterklassen usw. möchten, benötigen Sie eine Funktion, die dies für Sie erledigt.Hier ist eine einfache, lesbare Funktion, die rekursiv alle Unterklassen einer bestimmten Klasse findet:
quelle
all_subclasses
ein seinset
, um Duplikate zu beseitigen?A(object)
,B(A)
,C(A)
, undD(B, C)
.get_all_subclasses(A) == [B, C, D, D]
.Die einfachste Lösung in allgemeiner Form:
Und eine Klassenmethode für den Fall, dass Sie eine einzelne Klasse haben, von der Sie erben:
quelle
Python 3.6 -
__init_subclass__
Wie bereits erwähnt, können Sie das
__subclasses__
Attribut überprüfen , um die Liste der Unterklassen abzurufen. Seit Python 3.6 können Sie diese Attributerstellung ändern, indem Sie die__init_subclass__
Methode überschreiben .Auf diese Weise können Sie, wenn Sie wissen, was Sie tun, das Verhalten von
__subclasses__
Unterklassen überschreiben und in dieser Liste weglassen / hinzufügen.quelle
__init_subclass
die Klasse der Eltern auslösen .Hinweis: Ich sehe, dass jemand (nicht @unutbu) die referenzierte Antwort so geändert hat, dass sie nicht mehr verwendet wird. Daher
vars()['Foo']
gilt der primäre Punkt meines Beitrags nicht mehr.FWIW, hier ist, was ich damit gemeint habe, dass die Antwort von @ unutbu nur mit lokal definierten Klassen funktioniert - und dass die Verwendung von
eval()
stattvars()
mit jeder zugänglichen Klasse funktioniert, nicht nur mit den im aktuellen Bereich definierten.Für diejenigen, die es nicht mögen
eval()
, wird auch ein Weg gezeigt, dies zu vermeiden.Hier ist zunächst ein konkretes Beispiel, das das potenzielle Problem bei der Verwendung veranschaulicht
vars()
:Dies könnte verbessert werden, indem das
eval('ClassName')
Down in die definierte Funktion verschoben wird, was die Verwendung erleichtert, ohne dass die zusätzliche Allgemeinheit verloren geht, die durch die Verwendung erzielt wird,eval()
die im Gegensatz zuvars()
nicht kontextsensitiv ist:Schließlich ist es möglich und in einigen Fällen sogar wichtig, die Verwendung
eval()
aus Sicherheitsgründen zu vermeiden. Hier ist eine Version ohne diese:quelle
eval()
- jetzt besser?Eine viel kürzere Version, um eine Liste aller Unterklassen zu erhalten:
quelle
Wir können dies sicherlich leicht tun, wenn wir auf das Objekt selbst zugreifen, ja.
Einfach seinen Namen zu geben, ist eine schlechte Idee, da es mehrere Klassen desselben Namens geben kann, die sogar im selben Modul definiert sind.
Ich habe eine Implementierung für eine andere Antwort erstellt . Da diese Frage beantwortet wird und etwas eleganter ist als die anderen Lösungen hier, ist sie hier:
Verwendung:
quelle
Dies ist keine so gute Antwort wie die Verwendung der speziellen integrierten
__subclasses__()
Klassenmethode, die @unutbu erwähnt, daher präsentiere ich sie lediglich als Übung. Diesubclasses()
definierte Funktion gibt ein Wörterbuch zurück, das alle Unterklassennamen den Unterklassen selbst zuordnet.Ausgabe:
quelle
Hier ist eine Version ohne Rekursion:
Dies unterscheidet sich von anderen Implementierungen darin, dass die ursprüngliche Klasse zurückgegeben wird. Dies liegt daran, dass der Code dadurch einfacher wird und:
Wenn get_subclasses_gen etwas seltsam aussieht, liegt das daran, dass es durch Konvertieren einer rekursiven Implementierung in einen Schleifengenerator erstellt wurde:
quelle