Comment puis-je vérifier (au moment de l'exécution) si une classe est une sous-classe d'une autre?

196

Disons que j'ai un costume de classe et quatre sous-classes de costume: Heart, Spade, Diamond, Club.

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

J'ai une méthode qui reçoit un costume comme paramètre, qui est un objet de classe, pas une instance. Plus précisément, il ne peut recevoir qu'une seule des quatre valeurs: Coeur, Bêche, Diamant, Club. Comment puis-je faire une affirmation qui assure une telle chose? Quelque chose comme:

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

J'utilise Python 3.

snakile
la source
1
@Leopd: N'est-ce vraiment pas clair? J'ai indiqué exactement quelles sont les quatre valeurs possibles qui my_methodpeuvent être paramétrées: "il ne peut recevoir qu'une seule des quatre valeurs: Coeur, Bêche, Diamant, Club". Ces valeurs sont des objets de classe, pas des instances de classe. Cela me semble assez clair, bien que je suppose que vous avez raison sur le flou parce que les réponses couvrent les deux possibilités. Vous êtes plus que bienvenus pour modifier la question si vous avez un libellé plus clair pour cela. Merci pour le commentaire.
snakile du
@snakile oui ce n'est pas clair. En raison du fait de s'appuyer sur l'exactitude de l'expression de quiconque, la glace est mince dans ce sujet. De nombreux nouveaux arrivants ne peuvent pas obtenir la chose tout-est-un-objet-en-python, peuvent exprimer une chose mais en penser une autre. C'est une réalité et, à part la pureté, il est tout à fait rationnel de s'attendre à ce comportement de la part des nouveaux arrivants. Laisser votre réputation indique la seule indication directe de la justesse de votre expression ici, ou devrais-je dire «en termes d'exactitude». Je comprends le souhait de prendre en compte vos connaissances et il est toujours irrationnel de ne pas prendre en compte les nouveaux arrivants sans cesse renouvelés.
n611x007
1
@snakile that, et la chose qu'il peut être raisonnable d'utiliser une convention de dénomination qui suffixe avec ces noms de paramètres _class, ce qui les rend similaires suit_class. J'ai proposé une telle convention de dénomination dans une question pertinente .
n611x007
Suggérez d'ajouter à l'exemple de code quatre lignes my_method(Heart) my_method(Spade)...
Bob Stein
Pour les cas où la variable en cours de test n'est pas garantie d'être une classe, vous pouvez ajouter une condition inspect.isclassou simplement utiliser isinstance(myvar, type)en Python 3, comme issubclasscela soulèvera une erreur si elle est transmise à une non-classe. Voir cette réponse . J'aurais commenté la réponse ci-dessous, mais elle n'aurait jamais vu le jour.
totalhack

Réponses:

224

Vous pouvez utiliser issubclass()comme ça assert issubclass(suit, Suit).

Trevor Boyd Smith
la source
55
"Mais pourquoi voudriez-vous faire une telle chose?" - parce que vous avez une classe de conteneur dont vous devez vous assurer qu'elle est homogène, et la seule façon de le faire est de vérifier le type lors de l'insertion?
Adam Parkin
140
S'il y a une chose qui est constante sur Stack Overflow, c'est que toutes les questions avec une réponse qui implique isinstance ou issubclass seront également accompagnées de conférences sur la frappe de canard!
Ben Roberts
26
Je suis tombé sur cette question en essayant de comprendre comment détecter si mon type numpy est un flottant ou un int pour une application de traitement d'image. Si c'est un float, la convention est de normaliser entre 0.0 et 1.0, si c'est int alors la convention est de 0 à 255. Je pourrais passer par toutes sortes de contorsions pour essayer d'obtenir l'image à quack, mais c'est beaucoup plus simple de demandez simplement "êtes-vous un canard" et modifiez mes opérations en conséquence.
Omegaman
28
Les tests de sous-classe facilitent le test unitaire de nombreuses choses, en particulier les modèles de Django. "Python n'est pas Java." Pourquoi les programmeurs python doivent-ils avoir de telles puces sur leurs épaules?
Michael Bacon
20
Pas de vote positif, uniquement à cause de la remarque sans imagination et plutôt arrogante à la fin. Une explication sur les raisons pour lesquelles il n'est peut-être pas nécessaire de le faire serait plus conviviale et plus utile.
Michael Scheper
47

issubclass(class, classinfo)

Extrait:

Renvoie vrai si classest une sous-classe (directe, indirecte ou virtuelle) de classinfo.

Katriel
la source
26

Vous pouvez utiliser isinstancesi vous avez une instance ou issubclasssi vous avez une classe. Normalement, je pensais que c'était une mauvaise idée. Normalement, en Python, vous déterminez si un objet est capable de quelque chose en essayant de lui faire cette chose.

David Heffernan
la source
Et si tu découvres que tu ne peux pas faire ça avec ça? Attrapez-vous une exception et essayez-vous autre chose?
falseusername
2
@wrongusername: C'est la voie 'Pythonic', oui. Je pense que cette coutume a du mérite, donc je la respecte tant qu'elle garde mon code clair. Il y a une bonne discussion à ce sujet ici: stackoverflow.com/questions/7604636/…
Michael Scheper
8
@Michael Scheper: Je dois dire que si c'est la voie pythonique, alors je déteste vraiment la voie pythonique. OMI, les exceptions ne doivent JAMAIS être utilisées pour le flux de contrôle. Si vous pensez qu'une erreur peut se produire, protégez-vous contre elle ... ne la traitez pas comme un GOTO. Lien intéressant que vous avez publié, cependant
leviathanbadger
@ aboveyou00: On dirait que le genre de cas dont vous parlez viole "tant qu'il garde mon code clair". Cependant, je ne suis pas fan de toutes les coutumes pythoniques, car beaucoup de gens abusent du principe EAFP et finissent par créer des bogues difficiles à trouver.
Michael Scheper
Cette vérification est utile lors de la définition des contrats. Par exemple, un constructeur qui reçoit un objet: nous aimerions affirmer que l'objet reçu est une instance d'une classe donnée et, ce faisant, nous informons le lecteur de code de ce que le constructeur attend.
mastropi
21

La issubclass(sub, sup)fonction booléenne renvoie true si la sous sub- classe donnée est en effet une sous-classe de la superclasse sup.

Jameer Mulani
la source
9
La réponse sans une conférence erronée +1.
Gringo Suave
2

issubclass exemple exécutable minimal

Voici un exemple plus complet avec quelques assertions:

#!/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 en amont .

Testé en Python 3.5.2.

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

Vous pouvez utiliser la sous-classe intégrée. Mais la vérification de type est généralement considérée comme inutile car vous pouvez utiliser la frappe de canard.

XORcist
la source
1

Utiliser issubclass semblait être un moyen propre d'écrire des niveaux de journal. Cela semble un peu étrange de l'utiliser ... mais il semble plus propre que les autres options.

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
la source
0

Selon le document Python , nous pouvons également utiliser un class.__mro__attribut ou une class.mro()méthode:

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
la source
-5
#issubclass(child,parent)

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

print(issubclass(c,b))#it returns true
Vigneshwaran Narayanan
la source
1
Les réponses de code uniquement ne sont pas appréciées sur SO, vous devez toujours ajouter du texte explicatif au code. Cependant, cette réponse est superflue: elle n'ajoute aucune information qui n'a pas déjà été mentionnée dans les réponses précédentes.
PM 2Ring