Wie überprüfe ich (zur Laufzeit), ob eine Klasse eine Unterklasse einer anderen ist?

195

Nehmen wir an, ich habe einen Klassenanzug und vier Unterklassen von Anzügen: Herz, Spaten, Diamant, Verein.

class Suit:
   ...
class Heart(Suit):
   ...
class Spade(Suit):
   ...
class Diamond(Suit):
   ...
class Club(Suit):
   ...

Ich habe eine Methode, die eine Farbe als Parameter erhält, die ein Klassenobjekt ist, keine Instanz. Genauer gesagt kann es nur einen der vier Werte erhalten: Herz, Spaten, Diamant, Verein. Wie kann ich eine Aussage machen, die so etwas gewährleistet? Etwas wie:

def my_method(suit):
   assert(suit subclass of Suit)
   ...

Ich benutze Python 3.

snakile
quelle
1
@Leopd: Ist es wirklich nicht klar? Ich habe genau angegeben, welche vier möglichen Werte my_methodals Parameter erhalten werden können: "Es kann nur einen der vier Werte erhalten: Herz, Spaten, Diamant, Schläger". Diese Werte sind Klassenobjekte, keine Klasseninstanzen. Es scheint mir ziemlich klar zu sein, obwohl ich vermute, dass Sie in Bezug auf die Unbestimmtheit Recht haben, da die Antworten beide Möglichkeiten abdecken. Sie können die Frage gerne bearbeiten, wenn Sie eine klarere Formulierung dafür haben. Danke für den Kommentar.
Snakile
@ Snakile ja es ist unklar. Aufgrund des Vertrauens in die Richtigkeit des Selbstausdrucks eines Menschen ist in diesem Thema dünnes Eis. Viele Neulinge können nicht alles bekommen, was ein Objekt in Python ist, können eine Sache ausdrücken, aber eine andere denken. Das ist eine Realität und abgesehen von der Reinheit ist es ziemlich rational, dieses Verhalten von Neuankömmlingen zu erwarten. Wenn Sie Ihren Ruf verlassen, ist dies der einzige direkte Hinweis darauf, ob Ihr Ausdruck hier korrekt ist oder ob ich "in Bezug auf die Korrektheit" sagen sollte. Ich verstehe den Wunsch, Ihr Wissen zu berücksichtigen, und es ist immer noch irrational, die sich ständig erneuernden Neuankömmlinge nicht zu berücksichtigen.
n611x007
1
@snakile das, und die Sache, dass es sinnvoll sein kann, eine Namenskonvention zu verwenden, die solche Parameternamen mit Suffixen versehen, so _classdass sie wie suit_class. Ich habe eine solche Namenskonvention in einer relevanten Frage vorgeschlagen .
n611x007
Schlagen Sie vor, dem Beispielcode vier Zeilen hinzuzufügen my_method(Heart) my_method(Spade)...
Bob Stein
In Fällen, in denen nicht garantiert wird, dass die getestete Variable eine Klasse ist, können Sie eine Bedingung hinzufügen inspect.isclassoder einfach isinstance(myvar, type)in Python 3 verwenden, da dies issubclasseinen Fehler auslöst, wenn eine Nichtklasse übergeben wird. Siehe diese Antwort . Ich hätte die Antwort unten kommentiert, aber sie hätte nie das Licht der Welt erblickt.
Totalhack

Antworten:

223

Sie können verwendet werden, issubclass()wie diese assert issubclass(suit, Suit).

Trevor Boyd Smith
quelle
55
"Aber warum willst du so etwas tun?" - weil Sie eine Containerklasse haben, die Sie sicherstellen müssen, dass sie homogen ist, und die einzige Möglichkeit, dies zu tun, besteht darin, den Typ beim Einfügen zu überprüfen?
Adam Parkin
139
Wenn es eine Sache gibt, die bei Stack Overflow eine Konstante ist, dann werden alle Fragen mit einer Antwort, die isinstance oder issubclass impliziert, auch mit Vorträgen über das Tippen von Enten begleitet!
Ben Roberts
26
Ich bin auf diese Frage gestoßen, um herauszufinden, wie ich feststellen kann, ob mein numpy dtype ein float oder ein int für eine Bildverarbeitungsanwendung ist. Wenn es ein Float ist, ist die Konvention, zwischen 0,0 und 1,0 zu normalisieren, wenn es int ist, dann ist die Konvention 0 bis 255. Ich könnte alle möglichen Verzerrungen durchgehen, um zu versuchen, das Bild zum Quaken zu bringen, aber es ist viel einfacher Fragen Sie einfach "Sind Sie eine Ente?" und skalieren Sie meine Operationen entsprechend.
Omegaman
28
Das Testen von Unterklassen erleichtert das Testen vieler Dinge, insbesondere von Djangos Modellen, erheblich. "Python ist nicht Java." Warum müssen Python-Programmierer solche Chips auf ihren Schultern haben?
Michael Bacon
20
Nicht abstimmend, nur wegen der einfallslosen und eher arroganten Bemerkung am Ende. Eine Erklärung, warum dies möglicherweise nicht erforderlich ist, wäre freundlicher und hilfreicher.
Michael Scheper
47

issubclass(class, classinfo)

Auszug:

Geben Sie true zurück, wenn classes sich um eine Unterklasse (direkt, indirekt oder virtuell) von handelt classinfo.

Katriel
quelle
26

Sie können verwenden, isinstancewenn Sie eine Instanz haben oder issubclasswenn Sie eine Klasse haben. Normalerweise dachte es ist eine schlechte Idee. Normalerweise ermitteln Sie in Python, ob ein Objekt zu etwas fähig ist, indem Sie versuchen, das zu tun.

David Heffernan
quelle
Was ist, wenn Sie herausfinden, dass Sie das Ding damit nicht machen können? Fangen Sie eine Ausnahme und versuchen Sie etwas anderes?
Falscher Benutzername
2
@wrongusername: Das ist der 'Pythonic'-Weg, ja. Ich denke, dieser Brauch hat seine Berechtigung, also folge ich ihm, solange er meinen Code klar hält. Es gibt eine gute Diskussion darüber hier: stackoverflow.com/questions/7604636/…
Michael Scheper
8
@ Michael Scheper: Ich muss sagen, wenn das der pythonische Weg ist, dann hasse ich den pythonischen Weg wirklich. IMO, Ausnahmen sollten NIEMALS für den Kontrollfluss verwendet werden. Wenn Sie glauben, dass ein Fehler auftreten könnte, schützen Sie sich davor ... behandeln Sie ihn nicht wie ein GOTO. Interessanter Link, den Sie jedoch gepostet haben
Leviathanbadger
@ Aboveyou00: Klingt so, als würden Fälle, von denen Sie sprechen, "solange mein Code klar bleibt" verletzt. Ich bin jedoch kein Fan aller pythonischen Bräuche, da viele Leute das EAFP-Prinzip missbrauchen und am Ende schwer zu findende Fehler verursachen.
Michael Scheper
Diese Prüfung ist praktisch, wenn Sie Verträge definieren. Zum Beispiel ein Konstruktor, der ein Objekt empfängt: Wir möchten behaupten, dass das empfangene Objekt eine Instanz einer bestimmten Klasse ist, und informieren den Codeleser auf diese Weise darüber, was der Konstruktor erwartet.
Mastropi
21

Die issubclass(sub, sup)boolesche Funktion gibt true zurück, wenn die angegebene Unterklasse subtatsächlich eine Unterklasse der Oberklasse ist sup.

Jameer Mulani
quelle
9
Die Antwort ohne fehlgeleitete Vorlesung +1.
Gringo Suave
2

issubclass minimales lauffähiges Beispiel

Hier ist ein vollständigeres Beispiel mit einigen Behauptungen:

#!/usr/bin/env python3

class Base:
    pass

class Derived(Base):
    pass

base = Base()
derived = Derived()

# Basic usage.
assert issubclass(Derived, Base)
assert not issubclass(Base, Derived)

# True for same object.
assert issubclass(Base, Base)

# Cannot use object of class.
try:
    issubclass(derived, Base)
except TypeError:
    pass
else:
    assert False

# Do this instead.
assert isinstance(derived, Base)

GitHub stromaufwärts .

Getestet in Python 3.5.2.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
1

Sie können die integrierte issubclass verwenden. Die Typprüfung wird jedoch normalerweise als unnötig angesehen, da Sie die Ententypisierung verwenden können.

XORcist
quelle
1

Die Verwendung von issubclass schien eine saubere Möglichkeit zu sein, Googlevels zu schreiben. Es fühlt sich irgendwie seltsam an, es zu benutzen ... aber es scheint sauberer als andere Optionen.

class Error(object): pass
class Warn(Error): pass
class Info(Warn): pass
class Debug(Info): pass

class Logger():
    LEVEL = Info

    @staticmethod
    def log(text,level):
        if issubclass(Logger.LEVEL,level):
            print(text)
    @staticmethod
    def debug(text):
        Logger.log(text,Debug)   
    @staticmethod
    def info(text):
        Logger.log(text,Info)
    @staticmethod
    def warn(text):
        Logger.log(text,Warn)
    @staticmethod
    def error(text):
        Logger.log(text,Error)
Jon Donnelly
quelle
0

Laut Python-Dokument können wir auch class.__mro__Attribute oder class.mro()Methoden verwenden:

class Suit:
    pass
class Heart(Suit):
    pass
class Spade(Suit):
    pass
class Diamond(Suit):
    pass
class Club(Suit):
    pass

>>> Heart.mro()
[<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>]
>>> Heart.__mro__
(<class '__main__.Heart'>, <class '__main__.Suit'>, <class 'object'>)

Suit in Heart.mro()  # True
object in Heart.__mro__  # True
Spade in Heart.mro()  # False
YaOzI
quelle
-5
#issubclass(child,parent)

class a:
    pass
class b(a):
    pass
class c(b):
    pass

print(issubclass(c,b))#it returns true
Vigneshwaran Narayanan
quelle
1
Nur-Code-Antworten werden in SO nicht geschätzt. Sie sollten dem Code immer einen erläuternden Text hinzufügen. Diese Antwort ist jedoch überflüssig: Sie fügt keine Informationen hinzu, die in den vorherigen Antworten noch nicht erwähnt wurden.
PM 2Ring